darcs 2.12.5 → 2.14.0
raw patch · 343 files changed
+14797/−14939 lines, 343 filesdep +stmdep +temporarydep −transformers-compatdep ~FindBindep ~HTTPdep ~HUnitsetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies added: stm, temporary
Dependencies removed: transformers-compat
Dependency ranges changed: FindBin, HTTP, HUnit, QuickCheck, Win32, array, async, attoparsec, base, bytestring, cmdargs, containers, cryptohash, directory, fgl, filepath, graphviz, hashable, haskeline, mmap, mtl, network, old-time, parsec, process, random, shelly, tar, terminfo, test-framework, test-framework-hunit, test-framework-quickcheck2, time, transformers, unix, unix-compat, utf8-string, vector, zip-archive, zlib
API changes (from Hackage documentation)
Files
- CHANGELOG +52/−0
- Setup.hs +241/−0
- Setup.lhs +0/−382
- contrib/_darcs.zsh +26/−23
- contrib/darcs_completion +20/−5
- darcs.cabal +119/−167
- darcs/darcs.hs +9/−15
- harness/Darcs/Test/Email.hs +18/−8
- harness/Darcs/Test/HashedStorage.hs +501/−0
- harness/Darcs/Test/Misc.hs +35/−12
- harness/Darcs/Test/Misc/CommandLine.hs +41/−0
- harness/Darcs/Test/Misc/Encoding.hs +36/−0
- harness/Darcs/Test/Patch.hs +96/−84
- harness/Darcs/Test/Patch/Arbitrary/Generic.hs +3/−5
- harness/Darcs/Test/Patch/Arbitrary/PrimFileUUID.hs +73/−84
- harness/Darcs/Test/Patch/Arbitrary/PrimV1.hs +154/−56
- harness/Darcs/Test/Patch/Arbitrary/RepoPatchV1.hs +13/−33
- harness/Darcs/Test/Patch/Arbitrary/RepoPatchV2.hs +3/−4
- harness/Darcs/Test/Patch/Check.hs +3/−3
- harness/Darcs/Test/Patch/Examples/Set1.hs +5/−8
- harness/Darcs/Test/Patch/Examples/Set2Unwitnessed.hs +165/−174
- harness/Darcs/Test/Patch/FileUUIDModel.hs +73/−72
- harness/Darcs/Test/Patch/Info.hs +84/−58
- harness/Darcs/Test/Patch/Properties/Check.hs +30/−13
- harness/Darcs/Test/Patch/Properties/Generic.hs +265/−163
- harness/Darcs/Test/Patch/Properties/GenericUnwitnessed.hs +21/−23
- harness/Darcs/Test/Patch/Properties/RepoPatchV2.hs +14/−7
- harness/Darcs/Test/Patch/Properties/V1Set1.hs +11/−12
- harness/Darcs/Test/Patch/Properties/V1Set2.hs +46/−21
- harness/Darcs/Test/Patch/RepoModel.hs +2/−0
- harness/Darcs/Test/Patch/Selection.hs +2/−2
- harness/Darcs/Test/Patch/V1Model.hs +13/−17
- harness/Darcs/Test/Patch/WSub.hs +10/−7
- harness/Darcs/Test/Patch/WithState.hs +1/−1
- harness/Darcs/Test/Repository/Inventory.hs +159/−0
- harness/Darcs/Test/Util/TestResult.hs +22/−28
- harness/Storage/Hashed/Test.hs +0/−542
- harness/test.hs +46/−87
- release/distributed-context +1/−1
- release/distributed-version +1/−1
- src/Bundled/Posix.hsc +0/−116
- src/Darcs/Patch.hs +12/−22
- src/Darcs/Patch/Annotate.hs +112/−119
- src/Darcs/Patch/Apply.hs +3/−13
- src/Darcs/Patch/ApplyMonad.hs +4/−11
- src/Darcs/Patch/ApplyPatches.hs +4/−5
- src/Darcs/Patch/Bracketed/Instances.hs +4/−4
- src/Darcs/Patch/Bundle.hs +19/−24
- src/Darcs/Patch/Choices.hs +282/−208
- src/Darcs/Patch/Conflict.hs +26/−10
- src/Darcs/Patch/Depends.hs +53/−64
- src/Darcs/Patch/Dummy.hs +19/−11
- src/Darcs/Patch/Format.hs +5/−8
- src/Darcs/Patch/Index/Monad.hs +17/−5
- src/Darcs/Patch/Index/Types.hs +6/−17
- src/Darcs/Patch/Info.hs +115/−68
- src/Darcs/Patch/Invert.hs +5/−1
- src/Darcs/Patch/Match.hs +63/−56
- src/Darcs/Patch/Matchable.hs +4/−2
- src/Darcs/Patch/Merge.hs +54/−3
- src/Darcs/Patch/MonadProgress.hs +1/−1
- src/Darcs/Patch/Named.hs +76/−24
- src/Darcs/Patch/Named/Wrapped.hs +23/−14
- src/Darcs/Patch/OldDate.hs +0/−351
- src/Darcs/Patch/PatchInfoAnd.hs +31/−27
- src/Darcs/Patch/Patchy.hs +0/−36
- src/Darcs/Patch/Patchy/Instances.hs +0/−13
- src/Darcs/Patch/Permutations.hs +13/−16
- src/Darcs/Patch/Prim.hs +14/−15
- src/Darcs/Patch/Prim/Class.hs +35/−18
- src/Darcs/Patch/Prim/FileUUID.hs +2/−6
- src/Darcs/Patch/Prim/FileUUID/Apply.hs +67/−50
- src/Darcs/Patch/Prim/FileUUID/Commute.hs +48/−59
- src/Darcs/Patch/Prim/FileUUID/Core.hs +83/−84
- src/Darcs/Patch/Prim/FileUUID/ObjectMap.hs +36/−13
- src/Darcs/Patch/Prim/FileUUID/Read.hs +29/−25
- src/Darcs/Patch/Prim/FileUUID/Show.hs +91/−43
- src/Darcs/Patch/Prim/V1.hs +2/−12
- src/Darcs/Patch/Prim/V1/Apply.hs +135/−69
- src/Darcs/Patch/Prim/V1/Coalesce.hs +6/−7
- src/Darcs/Patch/Prim/V1/Commute.hs +41/−53
- src/Darcs/Patch/Prim/V1/Core.hs +11/−14
- src/Darcs/Patch/Prim/V1/Read.hs +62/−58
- src/Darcs/Patch/Prim/V1/Show.hs +28/−42
- src/Darcs/Patch/Progress.hs +0/−2
- src/Darcs/Patch/Read.hs +8/−8
- src/Darcs/Patch/Rebase.hs +45/−18
- src/Darcs/Patch/Rebase/Container.hs +11/−16
- src/Darcs/Patch/Rebase/Fixup.hs +6/−6
- src/Darcs/Patch/Rebase/Item.hs +14/−13
- src/Darcs/Patch/Rebase/Name.hs +14/−18
- src/Darcs/Patch/Rebase/Viewing.hs +40/−37
- src/Darcs/Patch/RepoPatch.hs +9/−7
- src/Darcs/Patch/Set.hs +10/−10
- src/Darcs/Patch/Show.hs +37/−24
- src/Darcs/Patch/Split.hs +64/−59
- src/Darcs/Patch/Summary.hs +25/−28
- src/Darcs/Patch/TokenReplace.hs +77/−48
- src/Darcs/Patch/TouchesFiles.hs +89/−65
- src/Darcs/Patch/V1.hs +2/−3
- src/Darcs/Patch/V1/Apply.hs +4/−1
- src/Darcs/Patch/V1/Commute.hs +20/−30
- src/Darcs/Patch/V1/Core.hs +9/−5
- src/Darcs/Patch/V1/Prim.hs +116/−0
- src/Darcs/Patch/V1/Show.hs +19/−14
- src/Darcs/Patch/V1/Viewing.hs +5/−11
- src/Darcs/Patch/V2.hs +4/−4
- src/Darcs/Patch/V2/Non.hs +40/−34
- src/Darcs/Patch/V2/Prim.hs +120/−0
- src/Darcs/Patch/V2/RepoPatch.hs +84/−91
- src/Darcs/Patch/Viewing.hs +38/−33
- src/Darcs/Patch/Witnesses/Eq.hs +2/−2
- src/Darcs/Patch/Witnesses/Ordered.hs +67/−25
- src/Darcs/Patch/Witnesses/Sealed.hs +4/−4
- src/Darcs/Patch/Witnesses/Show.hs +0/−2
- src/Darcs/Patch/Witnesses/Unsafe.hs +6/−7
- src/Darcs/Patch/Witnesses/WZipper.hs +0/−2
- src/Darcs/Prelude.hs +27/−9
- src/Darcs/Repository.hs +44/−134
- src/Darcs/Repository/ApplyPatches.hs +3/−3
- src/Darcs/Repository/Cache.hs +6/−19
- src/Darcs/Repository/Clone.hs +140/−191
- src/Darcs/Repository/Create.hs +165/−0
- src/Darcs/Repository/Diff.hs +32/−8
- src/Darcs/Repository/Flags.hs +12/−0
- src/Darcs/Repository/Format.hs +3/−6
- src/Darcs/Repository/Hashed.hs +1101/−0
- src/Darcs/Repository/HashedIO.hs +57/−55
- src/Darcs/Repository/HashedRepo.hs +0/−776
- src/Darcs/Repository/Identify.hs +229/−0
- src/Darcs/Repository/Internal.hs +0/−1092
- src/Darcs/Repository/InternalTypes.hs +47/−11
- src/Darcs/Repository/Inventory.hs +239/−0
- src/Darcs/Repository/Job.hs +85/−41
- src/Darcs/Repository/Match.hs +5/−16
- src/Darcs/Repository/Merge.hs +127/−73
- src/Darcs/Repository/Motd.hs +0/−46
- src/Darcs/Repository/Old.hs +47/−33
- src/Darcs/Repository/Packs.hs +106/−13
- src/Darcs/Repository/PatchIndex.hs +198/−227
- src/Darcs/Repository/Pending.hs +281/−19
- src/Darcs/Repository/Prefs.hs +184/−51
- src/Darcs/Repository/Rebase.hs +17/−18
- src/Darcs/Repository/Repair.hs +22/−24
- src/Darcs/Repository/Resolution.hs +0/−3
- src/Darcs/Repository/State.hs +342/−246
- src/Darcs/Repository/Test.hs +15/−21
- src/Darcs/Repository/Working.hs +75/−0
- src/Darcs/UI/ApplyPatches.hs +26/−24
- src/Darcs/UI/Commands.hs +44/−226
- src/Darcs/UI/Commands/Add.hs +34/−90
- src/Darcs/UI/Commands/Amend.hs +16/−24
- src/Darcs/UI/Commands/Annotate.hs +39/−65
- src/Darcs/UI/Commands/Apply.hs +59/−127
- src/Darcs/UI/Commands/Clone.hs +64/−90
- src/Darcs/UI/Commands/Convert.hs +206/−209
- src/Darcs/UI/Commands/Diff.hs +42/−73
- src/Darcs/UI/Commands/Dist.hs +47/−75
- src/Darcs/UI/Commands/GZCRCs.hs +26/−48
- src/Darcs/UI/Commands/Help.hs +59/−46
- src/Darcs/UI/Commands/Init.hs +22/−41
- src/Darcs/UI/Commands/Log.hs +86/−115
- src/Darcs/UI/Commands/MarkConflicts.hs +253/−86
- src/Darcs/UI/Commands/Move.hs +32/−55
- src/Darcs/UI/Commands/Optimize.hs +105/−212
- src/Darcs/UI/Commands/Pull.hs +109/−236
- src/Darcs/UI/Commands/Push.hs +71/−115
- src/Darcs/UI/Commands/Rebase.hs +200/−456
- src/Darcs/UI/Commands/Record.hs +18/−53
- src/Darcs/UI/Commands/Remove.hs +23/−45
- src/Darcs/UI/Commands/Repair.hs +118/−107
- src/Darcs/UI/Commands/Replace.hs +43/−66
- src/Darcs/UI/Commands/Revert.hs +20/−42
- src/Darcs/UI/Commands/Rollback.hs +32/−56
- src/Darcs/UI/Commands/Send.hs +271/−183
- src/Darcs/UI/Commands/SetPref.hs +14/−32
- src/Darcs/UI/Commands/Show.hs +2/−4
- src/Darcs/UI/Commands/ShowAuthors.hs +17/−32
- src/Darcs/UI/Commands/ShowBug.hs +0/−80
- src/Darcs/UI/Commands/ShowContents.hs +7/−28
- src/Darcs/UI/Commands/ShowDependencies.hs +21/−45
- src/Darcs/UI/Commands/ShowFiles.hs +32/−53
- src/Darcs/UI/Commands/ShowIndex.hs +32/−50
- src/Darcs/UI/Commands/ShowPatchIndex.hs +35/−51
- src/Darcs/UI/Commands/ShowRepo.hs +60/−65
- src/Darcs/UI/Commands/ShowTags.hs +10/−27
- src/Darcs/UI/Commands/Tag.hs +49/−77
- src/Darcs/UI/Commands/Test.hs +37/−71
- src/Darcs/UI/Commands/TransferMode.hs +6/−20
- src/Darcs/UI/Commands/Unrecord.hs +103/−174
- src/Darcs/UI/Commands/Unrevert.hs +25/−56
- src/Darcs/UI/Commands/Util.hs +127/−15
- src/Darcs/UI/Commands/Util/Tree.hs +21/−26
- src/Darcs/UI/Commands/WhatsNew.hs +181/−171
- src/Darcs/UI/CommandsAux.hs +0/−3
- src/Darcs/UI/Completion.hs +185/−0
- src/Darcs/UI/Defaults.hs +4/−6
- src/Darcs/UI/Email.hs +14/−11
- src/Darcs/UI/External.hs +81/−78
- src/Darcs/UI/Flags.hs +105/−188
- src/Darcs/UI/Message/Send.hs +0/−186
- src/Darcs/UI/Options.hs +5/−14
- src/Darcs/UI/Options/All.hs +249/−124
- src/Darcs/UI/Options/Core.hs +13/−0
- src/Darcs/UI/Options/Flags.hs +3/−2
- src/Darcs/UI/Options/Matching.hs +12/−6
- src/Darcs/UI/PatchHeader.hs +25/−31
- src/Darcs/UI/PrintPatch.hs +7/−10
- src/Darcs/UI/RunCommand.hs +31/−30
- src/Darcs/UI/SelectChanges.hs +234/−315
- src/Darcs/UI/TheCommands.hs +0/−1
- src/Darcs/UI/Usage.hs +161/−22
- src/Darcs/Util/AtExit.hs +0/−2
- src/Darcs/Util/Bug.hs +0/−30
- src/Darcs/Util/ByteString.hs +141/−271
- src/Darcs/Util/CommandLine.hs +1/−22
- src/Darcs/Util/Crypt/SHA1.hs +0/−225
- src/Darcs/Util/Crypt/SHA256.hs +0/−12
- src/Darcs/Util/DateMatcher.hs +39/−1
- src/Darcs/Util/DateTime.hs +0/−1
- src/Darcs/Util/Diff/Myers.hs +0/−4
- src/Darcs/Util/Diff/Patience.hs +0/−4
- src/Darcs/Util/Download.hs +10/−12
- src/Darcs/Util/Encoding.hs +32/−12
- src/Darcs/Util/Encoding/IConv.hsc +0/−185
- src/Darcs/Util/Encoding/Win32.hs +3/−0
- src/Darcs/Util/Environment.hs +0/−15
- src/Darcs/Util/External.hs +0/−1
- src/Darcs/Util/File.hs +5/−7
- src/Darcs/Util/Global.hs +0/−2
- src/Darcs/Util/Hash.hs +235/−29
- src/Darcs/Util/Index.hs +86/−62
- src/Darcs/Util/IsoDate.hs +23/−11
- src/Darcs/Util/Lock.hs +107/−62
- src/Darcs/Util/Path.hs +30/−32
- src/Darcs/Util/Printer.hs +92/−109
- src/Darcs/Util/Printer/Color.hs +48/−52
- src/Darcs/Util/Progress.hs +1/−4
- src/Darcs/Util/Ssh.hs +6/−5
- src/Darcs/Util/Text.hs +10/−2
- src/Darcs/Util/Tree.hs +16/−17
- src/Darcs/Util/Tree/Hashed.hs +38/−39
- src/Darcs/Util/Tree/Monad.hs +17/−17
- src/Darcs/Util/Tree/Plain.hs +6/−6
- src/Darcs/Util/URL.hs +0/−4
- src/fpstring.c +0/−69
- src/fpstring.h +0/−7
- src/h_iconv.c +0/−18
- src/h_iconv.h +0/−9
- src/impossible.h +0/−8
- src/win32/System/Posix/Files.hsc +13/−10
- src/win32/System/Posix/IO.hsc +1/−1
- tests/add.sh +12/−2
- tests/apply-hunks.sh +1/−4
- tests/apply.sh +0/−1
- tests/clone.sh +16/−15
- tests/conflict-doppleganger.sh +0/−2
- tests/conflict-fight.sh +1/−6
- tests/conflict-reporting.sh +0/−3
- tests/convert-darcs2.sh +64/−0
- tests/convert.sh +0/−62
- tests/convert_export.sh +7/−0
- tests/data/many-files--darcs-2.tgz binary
- tests/data/many-files--hashed.tgz binary
- tests/diff.sh +3/−0
- tests/directory_confusion.sh +0/−23
- tests/disable.sh +1/−1
- tests/failing-issue1014_identical_patches.sh +1/−1
- tests/failing-issue1363-mark-conflicts.sh +0/−62
- tests/failing-issue1401_bug_in_get_extra.sh +2/−9
- tests/failing-issue1610_get_extra.sh +1/−4
- tests/failing-issue2138-whatsnew-s.sh +59/−0
- tests/failing-issue2208-replace-fails-with-resolving-unrecorded-change.sh +0/−53
- tests/failing-issue2243-unknown-patch-annotating-empty-first-line.sh +0/−37
- tests/failing-issue2275_follows-symlinks.sh +45/−0
- tests/failing-issue2443-timestamp_index_keeps_unrecorded_addfile.sh +35/−0
- tests/failing-issue2480-display-unicode-in-patch-content.sh +0/−9
- tests/failing-issue2548-inconsistent-pending-after-merge.sh +43/−0
- tests/failing-look_for_replaces1.sh +1/−1
- tests/failing-look_for_replaces2.sh +0/−44
- tests/filename_with_spaces.sh +10/−0
- tests/hashed_inventory.sh +1/−1
- tests/hidden_conflict.sh +4/−4
- tests/issue1162_add_nonexistent_slash.sh +0/−16
- tests/issue1344_abort_early_cant_send.sh +6/−6
- tests/issue1739-escape-multibyte-chars-correctly.sh +3/−2
- tests/issue1978.sh +0/−8
- tests/issue2138-whatsnew-s.sh +0/−59
- tests/issue2160_wrong_line_number_when_appending_empty_line.sh +19/−0
- tests/issue2208-replace-fails-with-resolving-unrecorded-change.sh +56/−0
- tests/issue2243-unknown-patch-annotating-empty-first-line.sh +37/−0
- tests/issue2262-display_of_meta_data.sh +7/−0
- tests/issue2286-metadata-encoding.sh +16/−3
- tests/issue2382-mv-dir-to-file-confuses-darcs.sh +1/−1
- tests/issue2480-display-unicode-in-patch-content.sh +9/−0
- tests/issue2512-multiple-authors-clobbered-in-global-conf.sh +47/−0
- tests/issue2526-whatsnew-boring.sh +19/−0
- tests/issue2545_command-execution-via-ssh-uri.sh +49/−0
- tests/issue2567-darcs-whatsnew-unified.sh +41/−0
- tests/issue2575-revert_during_rebase.sh +11/−0
- tests/issue2581-rebase_pull_reorder_updates_format.sh +16/−0
- tests/issue279_get_extra.sh +4/−4
- tests/issue436.sh +4/−7
- tests/lazy-optimize-reorder.sh +1/−1
- tests/log-duplicate.sh +3/−4
- tests/log.sh +10/−16
- tests/look_for_moves_and_replaces.sh +70/−0
- tests/look_for_moves_with_args.sh +50/−0
- tests/merging_newlines.sh +5/−17
- tests/network/clone-http-packed-detect.sh +7/−15
- tests/network/clone-http-packed.sh +4/−12
- tests/network/failing-issue1599-automatically-expire-unused-caches.sh +55/−0
- tests/network/issue1599-automatically-expire-unused-caches.sh +0/−55
- tests/network/issue1932-remote.sh +1/−1
- tests/network/issue2090-transfer-mode.sh +1/−2
- tests/network/issue2545_command-execution-via-ssh-uri.sh +45/−0
- tests/network/ssh.sh +0/−1
- tests/network/sshlib +11/−12
- tests/oldfashioned.sh +1/−1
- tests/printer.sh +7/−4
- tests/pull.sh +19/−0
- tests/pull_many_files.sh +0/−29
- tests/push.sh +1/−1
- tests/rebase-apply.sh +1/−1
- tests/rebase-basic.sh +1/−1
- tests/rebase-nochanges.sh +1/−2
- tests/rebase-pull.sh +1/−1
- tests/repoformat.sh +2/−6
- tests/revert.sh +87/−0
- tests/revert_interactive.sh +0/−67
- tests/revert_unrecorded_add.sh +0/−13
- tests/send-output-v1.sh +1/−3
- tests/send-output-v2.sh +1/−3
- tests/several_commands.sh +0/−25
- tests/show-bug.sh +0/−22
- tests/show_files.sh +25/−0
- tests/split-patches.sh +0/−2
- tests/switch-encoding.sh +87/−0
- tests/toolbox.sh +0/−16
- tests/utf8-display.sh +2/−1
- tests/v1-braced.sh +1/−3
- tests/whatsnew-adds-no-summary.sh +51/−0
- tests/whatsnew.sh +7/−7
@@ -1,3 +1,55 @@+Darcs 2.14.0, 4 April 2018++ * fix encoding business, make DARCS_DONT_ESCAPE_8BIT=1 default (Ben, Ganesh Sittampalam)+ * show explicit dependencies in `darcs log -s` (Gian Piero Carrubba)+ * improve bash/zsh completion (Ben, Gian Piero)+ * no longer print an error message when ctrlc'ing pager (Guillaume Hoffmann)+ * `darcs help markdown` mentions all files in `_darcs/prefs/` (Guillaume)+ * add patch index status to `show repo` command (Ben)+ * per-file conflict marking (Ben Franksen)+ * make it possible to use DARCS_SCP=rsync (Ben)+ * add --not-in-remote option to unrecord command (Ben)+ * plug memory leak and improve efficiency in annotate (Ben)+ * save unneeded FL/RL reverses in SelectChanges module (Ben)+ * optimize token replace code and --look-for-replaces (Ben)+ * no longer show conflicting files on `whatsnew -s`, will reintrodue this+ feature when it is done efficiently (Guillaume)+ * separate display and storage of patches (Ben)+ * support GHC 8.2 and GHC 8.4 (Ganesh)+ * many refactorings in Darcs.Repository modules and API (Ben, Guillaume)+ * no longer track build dependencies in Setup.hs, nor use+ alpha, beta, rc names (Guillaume)+ * refactor `pull --reorder-patches` (Ben)+ * refactor SelectChanges (Ben)+ * remove Patchy typeclass and redundant constaints where possible (Guillaume)+ * fix build with cabal new-build (Francesco Ariis)+ * unit and quickcheck tests for inventories (Ben)+ * throw out all access to bytestring internals from Darcs.Util.ByteString (Ben)+ * refactor, simplify, and document hunk application (Ben)+ * drop support of old cache location and SHA1-hashed repos (Guillaume)+ * rely on GHC's own stack traces for bug reporting (Guillaume)+ * fixed the following bugs:+ * fix mail encoding with '.' or '=' as last character (Timo von Holtz)+ * 2526: whatsnew -l --boring should list boring files (Ben)+ * 2208: replace detects existing force hunks in working (Ben)+ * 2512: author name is written to repository after multiple-choice+ prompt (Stephan-A. Posselt)+ * 2359: convert --export mishandles Unicode filenames (Ben)+ * 2545: prevent argument smuggling in SSH repository URLs (Gian Piero)+ * 2581: fix rebase pull --reorder (Ben)+ * 2575: fix unrevert with rebase (Ben)+ * 2579: allow darcs send to work even if no MTA is installed+ * 2555: include explicit dependencies in the output of `log -v` (Gian Piero)+ * 2569: decoding multibyte characters (Ben)+ * 2563: create remote repo in correct format in ssh tests (Ben)+ * 2565: create _darcs dir after searching for an existing one (Ben)+ * 2567: darcs whatsnew --unified (Ben)+ * 2566: avoid renaming across file systems (Ben)+ * 2564: delete wrong and irrelevant propConcatPS (Guillaume)+ * 2559: remove trailing empty lines in patch header edition (Guillaume)+ * 2536: mask out internal matchers in `show files` routing logic (Gian Piero)++ Darcs 2.12.5, 11 January 2017 * Bump some dependency upper bounds (Ganesh Sittampalam)
@@ -0,0 +1,241 @@+-- copyright (c) 2008 Duncan Coutts+-- portions copyright (c) 2008 David Roundy+-- portions copyright (c) 2007-2009 Judah Jacobson++import Distribution.Simple+ ( defaultMainWithHooks, UserHooks(..), simpleUserHooks )+import Distribution.ModuleName( toFilePath )+import Distribution.PackageDescription+ ( PackageDescription(executables, testSuites), Executable(exeName)+ , emptyBuildInfo+ , TestSuite(testBuildInfo)+ , updatePackageDescription+ , cppOptions, ccOptions+ , library, libBuildInfo, otherModules )+import Distribution.Package+ ( packageVersion )+import Distribution.Version( Version )+import Distribution.Simple.LocalBuildInfo+ ( LocalBuildInfo(..), absoluteInstallDirs )+import Distribution.Simple.InstallDirs (mandir, CopyDest (NoCopyDest))+import Distribution.Simple.Setup+ (buildVerbosity, copyDest, copyVerbosity, fromFlag,+ haddockVerbosity, installVerbosity, sDistVerbosity, replVerbosity )+import Distribution.Simple.BuildPaths ( autogenModulesDir )+import Distribution.System+ ( OS(Windows), buildOS )+import Distribution.Simple.Utils+ (copyFiles, createDirectoryIfMissingVerbose, rawSystemStdout,+ rewriteFile )+import Distribution.Verbosity+ ( Verbosity )+import Distribution.Text+ ( display )+import Control.Monad ( unless, void )++import System.Directory+ ( doesDirectoryExist, doesFileExist )+import System.IO+ ( openFile, IOMode(..) )+import System.Process (runProcess)+import Data.List( isInfixOf, lines )+import System.FilePath ( (</>) )+import Foreign.Marshal.Utils ( with )+import Foreign.Storable ( peek )+import Foreign.Ptr ( castPtr )+import Data.Monoid ( mappend )+import Data.Word ( Word8, Word32 )++import qualified Control.Exception as Exception++catchAny :: IO a -> (Exception.SomeException -> IO a) -> IO a+catchAny f h = Exception.catch f (\e -> h (e :: Exception.SomeException))++main :: IO ()+main = defaultMainWithHooks $ simpleUserHooks {++ buildHook = \ pkg lbi hooks flags ->+ let verb = fromFlag $ buildVerbosity flags+ in commonBuildHook buildHook pkg lbi hooks verb >>= ($ flags),++ haddockHook = \ pkg lbi hooks flags ->+ let verb = fromFlag $ haddockVerbosity flags+ in commonBuildHook haddockHook pkg lbi hooks verb >>= ($ flags) ,+ replHook = \pkg lbi hooks flags args ->+ let verb = fromFlag $ replVerbosity flags+ in commonBuildHook replHook pkg lbi hooks verb >>= (\f -> f flags args) ,+ postBuild = \ _ _ _ lbi -> buildManpage lbi,+ postCopy = \ _ flags pkg lbi ->+ installManpage pkg lbi (fromFlag $ copyVerbosity flags) (fromFlag $ copyDest flags),+ postInst = \ _ flags pkg lbi ->+ installManpage pkg lbi (fromFlag $ installVerbosity flags) NoCopyDest,++ sDistHook = \ pkg lbi hooks flags -> do+ let pkgVer = packageVersion pkg+ verb = fromFlag $ sDistVerbosity flags+ x <- versionPatches verb pkgVer+ y <- context verb+ rewriteFile "release/distributed-version" $ show x+ rewriteFile "release/distributed-context" $ show y+ putStrLn "about to hand over"+ let pkg' = pkg { library = sanity (library pkg) }+ sanity (Just lib) = Just $ lib { libBuildInfo = sanity' $ libBuildInfo lib }+ sanity _ = error "eh"+ sanity' bi = bi { otherModules = [ m | m <- otherModules bi, toFilePath m /= "Version" ] }++ sDistHook simpleUserHooks pkg' lbi hooks flags+ ,+ postConf = \_ _ _ _ -> return () --- Usually this checked for external C+ --- dependencies, but we already have performed such+ --- check in the confHook+}++-- | For @./Setup build@ and @./Setup haddock@, do some unusual+-- things, then invoke the base behaviour ("simple hook").+commonBuildHook :: (UserHooks -> PackageDescription -> LocalBuildInfo -> t -> a)+ -> PackageDescription -> LocalBuildInfo -> t -> Verbosity -> IO a+commonBuildHook runHook pkg lbi hooks verbosity = do+ (version, state) <- determineVersion verbosity pkg++ -- Create our own context file.+ generateVersionModule verbosity lbi version state++ -- Add custom -DFOO[=BAR] flags to the cpp (for .hs) and cc (for .c)+ -- invocations, doing a dance to make the base hook aware of them.+ littleEndian <- testEndianness+ let args = ("-DPACKAGE_VERSION=" ++ show' version) :+ [arg | (arg, True) <- -- include fst iff snd.+ [-- We have MAPI iff building on/for Windows.+ ("-DHAVE_MAPI", buildOS == Windows),+ ("-DLITTLEENDIAN", littleEndian),+ ("-DBIGENDIAN", not littleEndian)]]+ bi = emptyBuildInfo { cppOptions = args, ccOptions = args }+ hbi = (Just bi, [(exeName exe, bi) | exe <- executables pkg])+ pkg' = updatePackageDescription hbi pkg++ -- updatePackageDescription doesn't handle test suites so we+ -- need to do this manually+ updateTestSuiteBI bi' testSuite+ = testSuite { testBuildInfo = bi' `mappend` testBuildInfo testSuite }+ pkg'' = pkg' { testSuites = map (updateTestSuiteBI bi) (testSuites pkg') }++ lbi' = lbi { localPkgDescr = pkg'' }+ return $ runHook simpleUserHooks pkg'' lbi' hooks++ where+ show' :: String -> String -- Petr was worried that we might+ show' = show -- allow non-String arguments.+ testEndianness :: IO Bool+ testEndianness = with (1 :: Word32) $ \p -> do o <- peek $ castPtr p+ return $ o == (1 :: Word8)++-- ---------------------------------------------------------------------+-- man page+-- ---------------------------------------------------------------------++buildManpage :: LocalBuildInfo -> IO ()+buildManpage lbi = do+ let darcs = buildDir lbi </> "darcs/darcs"+ manpage = buildDir lbi </> "darcs/darcs.1"+ manpageHandle <- openFile manpage WriteMode+ void $ runProcess darcs ["help","manpage"]+ Nothing Nothing Nothing (Just manpageHandle) Nothing++installManpage :: PackageDescription -> LocalBuildInfo+ -> Verbosity -> CopyDest -> IO ()+installManpage pkg lbi verbosity copy =+ copyFiles verbosity+ (mandir (absoluteInstallDirs pkg lbi copy) </> "man1")+ [(buildDir lbi </> "darcs", "darcs.1")]++-- ---------------------------------------------------------------------+-- version module+-- ---------------------------------------------------------------------++determineVersion :: Verbosity -> PackageDescription -> IO (String, String)+determineVersion verbosity pkg = do+ let darcsVersion = packageVersion pkg+ numPatches <- versionPatches verbosity darcsVersion+ return (display darcsVersion, versionStateString numPatches)++ where+ versionStateString :: Maybe Int -> String+ versionStateString Nothing = "unknown"+ versionStateString (Just 0) = "release"+ versionStateString (Just 1) = "+ 1 patch"+ versionStateString (Just n) = "+ " ++ show n ++ " patches"++versionPatches :: Verbosity -> Version -> IO (Maybe Int)+versionPatches verbosity darcsVersion = do+ numPatchesDarcs <- do+ out <- rawSystemStdout verbosity "darcs"+ ["log", "-a", "--from-tag", display darcsVersion, "--count"]+ case reads out of+ ((n,_):_) -> return $ Just ((n :: Int) - 1)+ _ -> return Nothing+ `catchAny` \_ -> return Nothing++ numPatchesDist <- parseFile versionFile+ return $ case (numPatchesDarcs, numPatchesDist) of+ (Just x, _) -> Just x+ (Nothing, Just x) -> Just x+ (Nothing, Nothing) -> Nothing++ where+ versionFile = "release/distributed-version"++generateVersionModule :: Verbosity -> LocalBuildInfo+ -> String -> String -> IO ()+generateVersionModule verbosity lbi version state = do+ let dir = autogenModulesDir lbi+ createDirectoryIfMissingVerbose verbosity True dir+ ctx <- context verbosity+ hash <- weakhash verbosity+ rewriteFile (dir </> "Version.hs") $ unlines+ ["module Version where"+ ,"version, weakhash, context :: String"+ ,"version = \"" ++ version ++ " (" ++ state ++ ")\""+ ,"weakhash = " ++ case hash of+ Just x -> show x+ Nothing -> show "not available"+ ,"context = " ++ case ctx of+ Just x -> show x+ Nothing -> show "context not available"+ ]++weakhash :: Verbosity -> IO (Maybe String)+weakhash verbosity = do+ inrepo <- doesDirectoryExist "_darcs"+ unless inrepo $ fail "Not a repository."+ out <- rawSystemStdout verbosity "darcs" ["show", "repo"]+ let line = filter ("Weak Hash:" `isInfixOf`) $ lines out+ return $ case (length line) of+ 0 -> Nothing+ _ -> Just $ last $ words $ head line+ `catchAny` \_ -> return Nothing++context :: Verbosity -> IO (Maybe String)+context verbosity = do+ contextDarcs <- do+ inrepo <- doesDirectoryExist "_darcs"+ unless inrepo $ fail "Not a repository."+ out <- rawSystemStdout verbosity "darcs" ["log", "-a", "--context"]+ return $ Just out+ `catchAny` \_ -> return Nothing++ contextDist <- parseFile contextFile+ return $ case (contextDarcs, contextDist) of+ (Just x, _) -> Just x+ (Nothing, Just x) -> Just x+ (Nothing, Nothing) -> Nothing+ where contextFile = "release/distributed-context"++parseFile :: (Read a) => String -> IO (Maybe a)+parseFile f = do+ exist <- doesFileExist f+ if exist then do+ content <- readFile f -- ^ ratify readFile: we don't care here.+ case reads content of+ ((s,_):_) -> return s+ _ -> return Nothing+ else return Nothing
@@ -1,382 +0,0 @@-\begin{code}-{-# LANGUAGE TemplateHaskell #-}--- copyright (c) 2008 Duncan Coutts--- portions copyright (c) 2008 David Roundy--- portions copyright (c) 2007-2009 Judah Jacobson--import qualified Distribution.InstalledPackageInfo as Installed-import Distribution.Simple- ( defaultMainWithHooks, UserHooks(..), simpleUserHooks )-import Distribution.Simple.Configure- ( checkForeignDeps )-import Distribution.ModuleName( toFilePath )-import Distribution.PackageDescription- ( PackageDescription(executables, testSuites), Executable(buildInfo, exeName)- , BuildInfo(customFieldsBI), emptyBuildInfo- , TestSuite(testBuildInfo)- , FlagName(FlagName)- , updatePackageDescription- , cppOptions, ccOptions, ldOptions- , library, libBuildInfo, otherModules- , extraLibs, extraLibDirs, includeDirs )-import Distribution.Package- ( packageVersion, packageName, PackageName(..), Package )-import Distribution.Version- ( Version(Version, versionBranch) )-import Data.Version( showVersion )-import Distribution.Simple.LocalBuildInfo- ( LocalBuildInfo(..), absoluteInstallDirs, externalPackageDeps )-import Distribution.Simple.InstallDirs (mandir, CopyDest (NoCopyDest))-import Distribution.Simple.PackageIndex ( topologicalOrder )-import Distribution.Simple.Program ( gccProgram, rawSystemProgramStdoutConf )-import Distribution.Simple.Setup- (buildVerbosity, copyDest, copyVerbosity, fromFlag,- haddockVerbosity, installVerbosity, sDistVerbosity,- configVerbosity, ConfigFlags, configConfigurationsFlags)-import qualified Distribution.Simple.Setup as DSS -- to get replVerbosity in Cabal > 1.18-import Distribution.Simple.BuildPaths- ( autogenModulesDir, exeExtension )-import Distribution.System- ( OS(Windows), buildOS )-import Distribution.Simple.Utils- (copyFiles, createDirectoryIfMissingVerbose, rawSystemStdout,- rewriteFile, withTempFile, cabalVersion)-import Distribution.Verbosity- ( Verbosity )-import Distribution.Text- ( display )-import Control.Monad ( zipWithM_, when, unless, filterM )-import Control.Exception ( bracket, handle, IOException )--import Language.Haskell.TH ( mkName, newName, recUpdE, varE, appE, lamE, varP )--import System.Directory- (copyFile, createDirectory, createDirectoryIfMissing,- doesDirectoryExist, doesFileExist,- getCurrentDirectory, getDirectoryContents,- removeDirectoryRecursive, removeFile, setCurrentDirectory,- getTemporaryDirectory- )-import System.Exit ( ExitCode(ExitSuccess) )-import System.IO- ( openFile, IOMode (..), stdout- , hPutStr, hFlush, hClose- )-import System.Process (runProcess)-import System.IO.Error ( isDoesNotExistError )-import Data.List( isPrefixOf, isSuffixOf, sort )-import System.Process( rawSystem )-import System.FilePath ( (</>), (<.>), splitDirectories, isAbsolute )-import Foreign.Marshal.Utils ( with )-import Foreign.Storable ( peek )-import Foreign.Ptr ( castPtr )-import Data.Monoid ( mappend )-import Data.Word ( Word8, Word32 )--import qualified Control.Exception as Exception--catchAny f h = Exception.catch f (\e -> h (e :: Exception.SomeException))--{- Template Haskell hackery for replHook while we want to support Cabal < 1.18 -}-replVerbosity =- $(if cabalVersion >= Version [1,18,0] []- then varE (mkName "DSS.replVerbosity")- else [| error "This shouldn't be called" |]- )--replHookBody replHookSel =- \pkg lbi hooks flags args ->- let verb = fromFlag $ replVerbosity flags- in commonBuildHook replHookSel pkg lbi hooks verb >>= (\f -> f flags args)--addReplHook =- $(if cabalVersion >= Version [1,18,0] []- then- do hooks <- newName "hooks"- let replHook = mkName "replHook"- app <- appE (varE (mkName "replHookBody")) (varE replHook)- lamE [varP hooks] (recUpdE (varE hooks) [return (replHook, app)])- else [| \hooks -> hooks |]- )-{- End of Template Haskell hackery -}--main :: IO ()-main = defaultMainWithHooks $ addReplHook $ simpleUserHooks {-- buildHook = \ pkg lbi hooks flags ->- let verb = fromFlag $ buildVerbosity flags- in commonBuildHook buildHook pkg lbi hooks verb >>= ($ flags),-- haddockHook = \ pkg lbi hooks flags ->- let verb = fromFlag $ haddockVerbosity flags- in commonBuildHook haddockHook pkg lbi hooks verb >>= ($ flags) ,-{-- -- this is the actual replHook code we want- replHook = \pkg lbi hooks flags args ->- let verb = fromFlag $ replVerbosity flags- in commonBuildHook replHook pkg lbi hooks verb >>= (\f -> f flags args) ,--}- postBuild = \ _ _ _ lbi -> buildManpage lbi,- postCopy = \ _ flags pkg lbi ->- installManpage pkg lbi (fromFlag $ copyVerbosity flags) (fromFlag $ copyDest flags),- postInst = \ _ flags pkg lbi ->- installManpage pkg lbi (fromFlag $ installVerbosity flags) NoCopyDest,-- sDistHook = \ pkg lbi hooks flags -> do- let pkgVer = packageVersion pkg- verb = fromFlag $ sDistVerbosity flags- x <- versionPatches verb pkgVer- y <- context verb pkgVer- rewriteFile "release/distributed-version" $ show x- rewriteFile "release/distributed-context" $ show y- putStrLn "about to hand over"- let pkg' = pkg { library = sanity (library pkg) }- sanity (Just lib) = Just $ lib { libBuildInfo = sanity' $ libBuildInfo lib }- sanity _ = error "eh"- sanity' bi = bi { otherModules = [ m | m <- otherModules bi, toFilePath m /= "Version" ] }-- sDistHook simpleUserHooks pkg' lbi hooks flags- ,- confHook =- if buildOS == Windows- then confHook simpleUserHooks- else- \genericDescript flags -> do- lbi <- confHook simpleUserHooks genericDescript flags- let pkgDescr = localPkgDescr lbi- let verb = fromFlag (configVerbosity flags)- checkForeignDeps pkgDescr lbi verb- let lib = maybe (error "darcs library was not configured - did it end up unbuildable?") id- (library pkgDescr)- let bi = libBuildInfo lib- bi' <- maybeSetLibiconv flags bi lbi- return lbi {localPkgDescr = pkgDescr {- library = Just lib {- libBuildInfo = bi'}}}- ,- postConf = \_ _ _ _ -> return () --- Usually this checked for external C- --- dependencies, but we already have performed such- --- check in the confHook-}---- | For @./Setup build@ and @./Setup haddock@, do some unusual--- things, then invoke the base behaviour ("simple hook").-commonBuildHook :: (UserHooks -> PackageDescription -> LocalBuildInfo -> t -> a)- -> PackageDescription -> LocalBuildInfo -> t -> Verbosity -> IO a-commonBuildHook runHook pkg lbi hooks verbosity = do- (version, state) <- determineVersion verbosity pkg-- -- Create our own context file.- generateVersionModule verbosity pkg lbi version state-- -- Add custom -DFOO[=BAR] flags to the cpp (for .hs) and cc (for .c)- -- invocations, doing a dance to make the base hook aware of them.- littleEndian <- testEndianness- let args = ("-DPACKAGE_VERSION=" ++ show' version) :- [arg | (arg, True) <- -- include fst iff snd.- [-- We have MAPI iff building on/for Windows.- ("-DHAVE_MAPI", buildOS == Windows),- ("-DLITTLEENDIAN", littleEndian),- ("-DBIGENDIAN", not littleEndian)]]- bi = emptyBuildInfo { cppOptions = args, ccOptions = args }- hbi = (Just bi, [(exeName exe, bi) | exe <- executables pkg])- pkg' = updatePackageDescription hbi pkg-- -- updatePackageDescription doesn't handle test suites so we- -- need to do this manually- updateTestSuiteBI bi testSuite- = testSuite { testBuildInfo = bi `mappend` testBuildInfo testSuite }- pkg'' = pkg' { testSuites = map (updateTestSuiteBI bi) (testSuites pkg') }-- lbi' = lbi { localPkgDescr = pkg'' }- return $ runHook simpleUserHooks pkg'' lbi' hooks-- where- customFields = map fst . customFieldsBI . buildInfo $ darcsExe- darcsExe = head [e | e <- executables pkg, exeName e == "darcs"]- show' :: String -> String -- Petr was worried that we might- show' = show -- allow non-String arguments.- testEndianness :: IO Bool- testEndianness = with (1 :: Word32) $ \p -> do o <- peek $ castPtr p- return $ o == (1 :: Word8)---- ------------------------------------------------------------------------ man page--- -----------------------------------------------------------------------buildManpage :: LocalBuildInfo -> IO ()-buildManpage lbi = do- let darcs = buildDir lbi </> "darcs/darcs"- manpage = buildDir lbi </> "darcs/darcs.1"- manpageHandle <- openFile manpage WriteMode- runProcess darcs ["help","manpage"]- Nothing Nothing Nothing (Just manpageHandle) Nothing- return ()--installManpage :: PackageDescription -> LocalBuildInfo- -> Verbosity -> CopyDest -> IO ()-installManpage pkg lbi verbosity copy =- copyFiles verbosity- (mandir (absoluteInstallDirs pkg lbi copy) </> "man1")- [(buildDir lbi </> "darcs", "darcs.1")]---- ------------------------------------------------------------------------ version module--- -----------------------------------------------------------------------determineVersion :: Verbosity -> PackageDescription -> IO (String, String)-determineVersion verbosity pkg = do- let darcsVersion = packageVersion pkg- numPatches <- versionPatches verbosity darcsVersion- return (display darcsVersion, versionStateString numPatches darcsVersion)-- where- versionStateString :: Maybe Int -> Version -> String- versionStateString Nothing _ = "unknown"- versionStateString (Just 0) v = case versionBranch v of- x | 97 `elem` x -> "alpha " ++ show (after 97 x)- | 98 `elem` x -> "beta " ++ show (after 98 x)- | 99 `elem` x ->- "release candidate " ++ show (after 99 x)- _ -> "release"- versionStateString (Just 1) _ = "+ 1 patch"- versionStateString (Just n) _ = "+ " ++ show n ++ " patches"- after w (x:r) | w == x = head r- | otherwise = after w r- after _ [] = undefined--versionPatches :: Verbosity -> Version -> IO (Maybe Int)-versionPatches verbosity darcsVersion = do- numPatchesDarcs <- do- out <- rawSystemStdout verbosity "darcs"- ["log", "-a", "--from-tag", display darcsVersion, "--count"]- case reads (out) of- ((n,_):_) -> return $ Just ((n :: Int) - 1)- _ -> return Nothing- `catchAny` \_ -> return Nothing-- numPatchesDist <- parseFile versionFile- return $ case (numPatchesDarcs, numPatchesDist) of- (Just x, _) -> Just x- (Nothing, Just x) -> Just x- (Nothing, Nothing) -> Nothing-- where- versionFile = "release/distributed-version"--generateVersionModule :: Verbosity -> PackageDescription -> LocalBuildInfo- -> String -> String -> IO ()-generateVersionModule verbosity pkg lbi version state = do- let dir = autogenModulesDir lbi- createDirectoryIfMissingVerbose verbosity True dir- ctx <- context verbosity (packageVersion pkg)- rewriteFile (dir </> "Version.hs") $ unlines- ["module Version where"- ,"builddeps, version, context :: String"- ,"version = \"" ++ version ++ " (" ++ state ++ ")\""- ,"builddeps = " ++ show ( formatdeps (externalPackageDeps lbi))- ,"context = " ++ case ctx of- Just x -> show x- Nothing -> show "context not available"- ]- where formatdeps = unlines . map (formatone . snd)- formatone p = case packageName p of PackageName n -> n ++ "-" ++ showVersion (packageVersion p)--context :: Verbosity -> Version -> IO (Maybe String)-context verbosity version = do- contextDarcs <- do- inrepo <- doesDirectoryExist "_darcs"- unless inrepo $ fail "Not a repository."- out <- rawSystemStdout verbosity "darcs" ["log", "-a", "--context"]- return $ Just out- `catchAny` \_ -> return Nothing-- contextDist <- parseFile contextFile- return $ case (contextDarcs, contextDist) of- (Just x, _) -> Just x- (Nothing, Just x) -> Just x- (Nothing, Nothing) -> Nothing- where contextFile = "release/distributed-context"--parseFile :: (Read a) => String -> IO (Maybe a)-parseFile f = do- exist <- doesFileExist f- if exist then do- content <- readFile f -- ^ ratify readFile: we don't care here.- case reads content of- ((s,_):_) -> return s- _ -> return Nothing- else return Nothing---- Test whether compiling a c program that links against libiconv needs -liconv.-maybeSetLibiconv :: ConfigFlags -> BuildInfo -> LocalBuildInfo -> IO BuildInfo-maybeSetLibiconv flags bi lbi = do- let biWithIconv = addIconv bi- let verb = fromFlag (configVerbosity flags)- if hasFlagSet flags (FlagName "libiconv")- then do- putStrLn "Using -liconv."- return biWithIconv- else do- putStr "checking whether to use -liconv... "- hFlush stdout- worksWithout <- tryCompile iconv_prog bi lbi verb- if worksWithout- then do- putStrLn "not needed."- return bi- else do- worksWith <- tryCompile iconv_prog biWithIconv lbi verb- if worksWith- then do- putStrLn "using -liconv."- return biWithIconv- else error "Unable to link against the iconv library."--hasFlagSet :: ConfigFlags -> FlagName -> Bool-hasFlagSet cflags flag = Just True == lookup flag (configConfigurationsFlags cflags)--tryCompile :: String -> BuildInfo -> LocalBuildInfo -> Verbosity -> IO Bool-tryCompile program bi lbi verb = handle processExit $ handle processException $ do- tempDir <- getTemporaryDirectory- withTempFile tempDir ".c" $ \fname cH ->- withTempFile tempDir "" $ \execName oH -> do- hPutStr cH program- hClose cH- hClose oH- -- TODO take verbosity from the args.- rawSystemProgramStdoutConf verb gccProgram (withPrograms lbi)- (fname : "-o" : execName : args)- return True- where- processException :: IOException -> IO Bool- processException e = return False- processExit = return . (==ExitSuccess)- -- Mimicing Distribution.Simple.Configure- deps = topologicalOrder (installedPkgs lbi)- args = concat- [ ccOptions bi- , cppOptions bi- , ldOptions bi- -- --extra-include-dirs and --extra-lib-dirs are included- -- in the below fields.- -- Also sometimes a dependency like rts points to a nonstandard- -- include/lib directory where iconv can be found. - , map ("-I" ++) (includeDirs bi ++ concatMap Installed.includeDirs deps)- , map ("-L" ++) (extraLibDirs bi ++ concatMap Installed.libraryDirs deps)- , map ("-l" ++) (extraLibs bi)- ]--addIconv :: BuildInfo -> BuildInfo-addIconv bi = bi {extraLibs = "iconv" : extraLibs bi}--iconv_prog :: String-iconv_prog = unlines - [ "#include <iconv.h>"- , "int main(void) {"- , " iconv_t t = iconv_open(\"UTF-8\", \"UTF-8\");"- , " return 0;"- , "}"- ]--\end{code}
@@ -1,35 +1,38 @@ #compdef darcs-## Darcs completion snippet for zsh.+## Darcs completion for zsh. ##+## Originally derived from a version by ## Copyright (C) 2009 Nicolas Pouillard-##-## 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 of the License, 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. +local -a darcs_options darcs_non_options darcs_arguments+ if (($CURRENT == 2)); then- # We're completing the first word after "darcs" -- the command.- _wanted command expl 'darcs command' \- compadd -- $( darcs --commands )+ compadd -- $(darcs --commands) else+ case "${words[2]}"; in+ get|clone)+ _urls+ ;;+ esac case "${words[$CURRENT]}"; in- # If it looks like an URL...- ht*|ft*)- _arguments '*:URL:_urls'- ;;- # If it looks like an explicit path... /*|./*|\~*|../*)- _arguments '*:file:_files'- ;;- # Otherwise, let's ask darcs for all possible options+ _files+ ;;+ -*)+ # advanced zsh (array) parameter expansion fu:+ # - ${(f)...} means split into array elements at line endings+ # instead of white space+ # - ${arr:#pat} drops elements matching pat from arr, whereas+ # ${(M)arr:#pat} drops non-matching elements+ # - ${arr/pat/repl} replaces pat with repl for all elements of arr+ darcs_arguments=(${(f)"$(words[$CURRENT]=--list-options && $words 2>/dev/null)"})+ darcs_options=(${${(M)darcs_arguments:#-*}/;/:})+ _describe '' darcs_options+ ;; *)- _arguments '*: :($(words[$CURRENT]="--list-options" && $words))'+ darcs_arguments=(${(f)"$(words[$CURRENT]=--list-options && $words 2>/dev/null)"})+ darcs_non_options=(${darcs_arguments:#-*})+ _multi_parts -i -S ' ' / darcs_non_options ;; esac fi
@@ -16,9 +16,27 @@ return 0 fi - local IFS=$'\n' # So that the following "command-output to array" operation splits only at newlines, not at each space, tab or newline.- COMPREPLY=( $( darcs ${COMP_WORDS[1]} --list-option | command grep "^${cur//./\\.}") )+ # Store the whole command line substituting the (possibly empty)+ # to-be-completed word with '--list-options'.+ local -a words=("${COMP_WORDS[@]}")+ words[$COMP_CWORD]="--list-options" + # Options are processed from left to right, so avoid to display the help+ # page when trying to complete a command line that includes '--help'. It+ # could be tricked by things like '--repodir --hell', but, come on... you+ # don't deserve a working completion if you name a directory '--hell'.+ for w in "${words[@]}"; do+ case "$w" in+ (--he*) return 0;;+ esac+ done++ # So that the following "command-output to array" operation splits only at+ # newlines, not at each space, tab or newline.+ local IFS=$'\n'+ COMPREPLY=( $( "${words[@]}" 2>/dev/null |\+ command grep "^${cur//./\\.}" | cut -d ';' -f 1) )+ # Then, we adapt the resulting strings to be reusable by bash. If we don't # do this, in the case where we have two repositories named # ~/space in there-0.1 and ~/space in there-0.2, the first completion will@@ -42,11 +60,8 @@ local colonprefixes=${cur%"${cur##*:}"} while [ $((--i)) -ge 0 ]; do COMPREPLY[$i]=`printf %q "${COMPREPLY[$i]}"`- COMPREPLY[$i]=${COMPREPLY[$i]#"$colonprefixes"} done return 0- } complete -F _darcs -o default darcs-
@@ -1,6 +1,6 @@ Name: darcs-version: 2.12.5-License: GPL+version: 2.14.0+License: GPL-2 License-file: COPYING Author: David Roundy <droundy@darcs.net>, <darcs-devel@darcs.net> Maintainer: <darcs-devel@darcs.net>@@ -33,7 +33,7 @@ Homepage: http://darcs.net/ Build-Type: Custom-Cabal-Version: >= 1.16+Cabal-Version: >= 1.24 extra-source-files: -- C headers@@ -107,16 +107,16 @@ manual: True description: Build with warnings-as-errors --- 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.-flag libiconv- Description: Explicitly link against the libiconv library.- Default: False+-- ----------------------------------------------------------------------+-- setup+-- ---------------------------------------------------------------------- -flag network-uri- description: Get Network.URI from the network-uri package- default: True+custom-setup+ setup-depends: base >= 4.9 && < 4.12,+ Cabal >= 1.24,+ process >= 1.2.3.0 && < 1.7,+ filepath >= 1.4.1 && < 1.5.0.0,+ directory >= 1.2.6.2 && < 1.4 -- ---------------------------------------------------------------------- -- darcs library@@ -158,10 +158,7 @@ Darcs.Patch.MonadProgress Darcs.Patch.Named Darcs.Patch.Named.Wrapped- Darcs.Patch.OldDate Darcs.Patch.PatchInfoAnd- Darcs.Patch.Patchy- Darcs.Patch.Patchy.Instances Darcs.Patch.Permutations Darcs.Patch.Prim Darcs.Patch.Prim.Class@@ -208,11 +205,13 @@ Darcs.Patch.V1.Apply Darcs.Patch.V1.Commute Darcs.Patch.V1.Core+ Darcs.Patch.V1.Prim Darcs.Patch.V1.Read Darcs.Patch.V1.Show Darcs.Patch.V1.Viewing Darcs.Patch.V2 Darcs.Patch.V2.Non+ Darcs.Patch.V2.Prim Darcs.Patch.V2.RepoPatch Darcs.Patch.Witnesses.Eq Darcs.Patch.Witnesses.Ordered@@ -225,18 +224,19 @@ Darcs.Repository.ApplyPatches Darcs.Repository.Cache Darcs.Repository.Clone+ Darcs.Repository.Create Darcs.Repository.PatchIndex Darcs.Repository.Diff Darcs.Repository.Flags Darcs.Repository.Format Darcs.Repository.HashedIO- Darcs.Repository.HashedRepo- Darcs.Repository.Internal+ Darcs.Repository.Hashed+ Darcs.Repository.Inventory+ Darcs.Repository.Identify Darcs.Repository.Job Darcs.Repository.Merge Darcs.Repository.InternalTypes Darcs.Repository.Match- Darcs.Repository.Motd Darcs.Repository.Old Darcs.Repository.Packs Darcs.Repository.Pending@@ -246,6 +246,7 @@ Darcs.Repository.Resolution Darcs.Repository.State Darcs.Repository.Test+ Darcs.Repository.Working Darcs.UI.ApplyPatches Darcs.UI.Commands Darcs.UI.Commands.Add@@ -277,7 +278,6 @@ Darcs.UI.Commands.SetPref Darcs.UI.Commands.Show Darcs.UI.Commands.ShowAuthors- Darcs.UI.Commands.ShowBug Darcs.UI.Commands.ShowContents Darcs.UI.Commands.ShowDependencies Darcs.UI.Commands.ShowFiles@@ -293,6 +293,7 @@ Darcs.UI.Commands.Unrecord Darcs.UI.Commands.Unrevert Darcs.UI.Commands.WhatsNew+ Darcs.UI.Completion Darcs.UI.Email Darcs.UI.External Darcs.UI.Defaults@@ -311,14 +312,10 @@ Darcs.UI.SelectChanges Darcs.UI.TheCommands Darcs.UI.Usage- Darcs.UI.Message.Send Darcs.Util.AtExit- Darcs.Util.Bug Darcs.Util.ByteString Darcs.Util.CommandLine Darcs.Util.Compat- Darcs.Util.Crypt.SHA1- Darcs.Util.Crypt.SHA256 Darcs.Util.DateMatcher Darcs.Util.DateTime Darcs.Util.Diff@@ -327,8 +324,8 @@ Darcs.Util.Download Darcs.Util.Download.Request Darcs.Util.Download.HTTP+ Darcs.Util.Encoding Darcs.Util.English- Darcs.Util.Environment Darcs.Util.Exception Darcs.Util.Exec Darcs.Util.External@@ -354,14 +351,11 @@ Darcs.Util.Tree.Plain Darcs.Util.URL Darcs.Util.Workaround- Bundled.Posix other-modules: Version Darcs.Util.Download.Curl- Darcs.Util.Encoding c-sources: src/atomic_create.c- src/fpstring.c src/maybe_relink.c src/umask.c src/system_encoding.c@@ -382,98 +376,81 @@ System.Posix.IO cpp-options: -DWIN32 c-sources: src/win32/send_email.c- build-depends: Win32 >= 2.3 && < 2.4+ build-depends: Win32 >= 2.3.1 && < 2.4 else- other-modules: Darcs.Util.Encoding.IConv- c-sources: src/h_iconv.c- build-depends: unix >= 2.6.0.1 && < 2.8+ build-depends: unix >= 2.7.1.0 && < 2.8 - build-depends: base >= 4.8 && < 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 && < 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,- 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.10.0.1 && < 0.13,- tar >= 0.4 && < 0.6,- data-ordlist == 0.4.*,- attoparsec >= 0.11 && < 0.14,- zip-archive >= 0.2.3 && < 0.4,- async >= 2.0.1.4 && < 2.2,- sandi >= 0.4 && < 0.5,- unix-compat >= 0.1.2 && < 0.5,- bytestring >= 0.10.0.2 && < 0.11,- old-time >= 1.1 && < 1.2,- time >= 1.5 && < 1.8,- -- 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- text >= 1.2.1.3 && < 1.3,- directory >= 1.2.0.1 && < 1.4.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+ build-depends: base >= 4.9 && < 4.12,+ stm >= 2.1 && < 2.5,+ binary >= 0.5 && < 0.9,+ containers >= 0.5.6.2 && < 0.6,+ regex-compat-tdfa >= 0.95.1 && < 0.96,+ regex-applicative >= 0.2 && < 0.4,+ mtl >= 2.2.1 && < 2.3,+ transformers >= 0.4.2.0 && < 0.6,+ parsec >= 3.1.9 && < 3.2,+ fgl >= 5.5.2.3 && < 5.6,+ graphviz >= 2999.18.1 && < 2999.20,+ html >= 1.0.1.2 && < 1.1,+ filepath >= 1.4.1 && < 1.5.0.0,+ haskeline >= 0.7.2 && < 0.8,+ cryptohash >= 0.11 && < 0.12,+ base16-bytestring >= 0.1 && < 0.2,+ utf8-string >= 1 && < 1.1,+ vector >= 0.11 && < 0.13,+ tar >= 0.5 && < 0.6,+ data-ordlist == 0.4.*,+ attoparsec >= 0.13.0.1 && < 0.14,+ zip-archive >= 0.3 && < 0.4,+ async >= 2.0.2 && < 2.2,+ sandi >= 0.4 && < 0.5,+ unix-compat >= 0.4.2 && < 0.6,+ bytestring >= 0.10.6 && < 0.11,+ old-time >= 1.1.0.3 && < 1.2,+ time >= 1.5.0.1 && < 1.9,+ text >= 1.2.1.3 && < 1.3,+ directory >= 1.2.6.2 && < 1.4,+ temporary >= 1.2.0.4 && < 1.3,+ process >= 1.2.3.0 && < 1.7,+ array >= 0.5.1.0 && < 0.6,+ random >= 1.1 && < 1.2,+ hashable >= 1.2.3.3 && < 1.3,+ mmap >= 0.5.9 && < 0.6,+ zlib >= 0.6.1.2 && < 0.7.0.0,+ network-uri == 2.6.*,+ network == 2.6.*,+ HTTP >= 4000.2.20 && < 4000.4 if flag(optimize)- ghc-options: -O2+ ghc-options: -O2 else- ghc-options: -O0+ ghc-options: -O0 if flag(warn-as-error)- 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 -fno-warn-dodgy-imports+ ghc-options: -Werror - if impl(ghc>=8.0)- -- This warning is new in GHC 8.0 and is not easy to fix- ghc-options: -fno-warn-redundant-constraints+ ghc-options: -Wall -funbox-strict-fields -fwarn-tabs if flag(curl)- cpp-options: -DHAVE_CURL- c-sources: src/hscurl.c- cc-options: -DHAVE_CURL+ cpp-options: -DHAVE_CURL+ c-sources: src/hscurl.c+ cc-options: -DHAVE_CURL if flag(pkgconfig)- pkgconfig-depends: libcurl+ pkgconfig-depends: libcurl else- extra-libraries: curl- includes: curl/curl.h-- if flag(network-uri)- build-depends: network-uri == 2.6.*, network == 2.6.*- else- build-depends: network >= 2.4.1.2 && < 2.6-- build-depends: HTTP >= 4000.2.8 && < 4000.4+ extra-libraries: curl+ includes: curl/curl.h -- The terminfo package cannot be built on Windows. if flag(terminfo) && !os(windows)- build-depends: terminfo >= 0.3 && < 0.5- cpp-options: -DHAVE_TERMINFO+ build-depends: terminfo >= 0.4.0.2 && < 0.5+ cpp-options: -DHAVE_TERMINFO - -- if true to work around cabal bug with flag ordering- if true- default-extensions:+ default-extensions: BangPatterns PatternGuards- GADTs+ GADTSyntax+ ExistentialQuantification TypeOperators FlexibleContexts FlexibleInstances@@ -501,50 +478,29 @@ hs-source-dirs: darcs if flag(optimize)- ghc-options: -O2+ ghc-options: -O2 else- ghc-options: -O0+ ghc-options: -O0 if flag(warn-as-error)- ghc-options: -Werror+ ghc-options: -Werror - -- Note: "if true" works around a cabal bug with order of flag composition- if true- ghc-options: -Wall -funbox-strict-fields -fwarn-tabs -fno-warn-dodgy-imports+ ghc-options: -Wall -funbox-strict-fields -fwarn-tabs if flag(threaded) ghc-options: -threaded if flag(static)- ghc-options: -static -optl-static -optl-pthread+ ghc-options: -static -optl-static -optl-pthread if flag(rts)- ghc-options: -rtsopts-- if impl(ghc>=8.0)- -- This warning is new in GHC 8.0 and is not easy to fix- ghc-options: -fno-warn-redundant-constraints+ ghc-options: -rtsopts -- see http://bugs.darcs.net/issue1037 cc-options: -D_REENTRANT - build-depends: darcs,- base >= 4.8 && < 4.10-- -- if true to work around cabal bug with flag ordering- if true- default-extensions:- BangPatterns- PatternGuards- GADTs- TypeOperators- FlexibleContexts- FlexibleInstances- ScopedTypeVariables- KindSignatures- RankNTypes- TypeFamilies- NoMonoLocalBinds+ build-depends: darcs,+ base >= 4.9 && < 4.12 -- ---------------------------------------------------------------------- -- unit test driver@@ -558,32 +514,33 @@ hs-source-dirs: harness if os(windows)- cpp-options: -DWIN32- build-depends: Win32 >= 2.2 && < 2.4+ cpp-options: -DWIN32+ build-depends: Win32 >= 2.3.1 && < 2.4 - build-depends: darcs,- base >= 4.8 && < 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.3.0.1 && < 1.5.0.0,- mtl >= 2.1 && < 2.3,- shelly >= 1.6.2 && < 1.7,- split >= 0.2.2 && < 0.3,- text >= 1.2.1.3 && < 1.3,- directory >= 1.2.0.1 && < 1.4.0.0,- FindBin >= 0.0 && < 0.1,- QuickCheck >= 2.6 && < 2.10,- HUnit >= 1.2.5.2 && < 1.6,- test-framework >= 0.4.0 && < 0.9,- test-framework-hunit >= 0.2.2 && < 0.4,- test-framework-quickcheck2 >= 0.3 && < 0.4,- zip-archive >= 0.2.3 && < 0.4+ build-depends: darcs,+ base >= 4.9 && < 4.12,+ array >= 0.5.1.0 && < 0.6,+ bytestring >= 0.10.6 && < 0.11,+ cmdargs >= 0.10.10 && < 0.11,+ containers >= 0.5.6.2 && < 0.6,+ filepath >= 1.4.1 && < 1.5.0.0,+ mtl >= 2.2.1 && < 2.3,+ shelly >= 1.6.8 && < 1.7,+ split >= 0.2.2 && < 0.3,+ text >= 1.2.1.3 && < 1.3,+ directory >= 1.2.6.2 && < 1.4,+ FindBin >= 0.0.5 && < 0.1,+ QuickCheck >= 2.8.2 && < 2.11,+ HUnit >= 1.3 && < 1.7,+ test-framework >= 0.8.1.1 && < 0.9,+ test-framework-hunit >= 0.3.0.2 && < 0.4,+ test-framework-quickcheck2 >= 0.3.0.3 && < 0.4,+ zip-archive >= 0.3 && < 0.4 -- list all unit test modules not exported by libdarcs; otherwise Cabal won't -- include them in the tarball other-modules: Darcs.Test.Email+ Darcs.Test.HashedStorage Darcs.Test.Patch.Check Darcs.Test.Patch.Examples.Set1 Darcs.Test.Patch.Examples.Set2Unwitnessed@@ -609,37 +566,34 @@ Darcs.Test.Patch.WithState Darcs.Test.Patch Darcs.Test.Misc+ Darcs.Test.Misc.CommandLine+ Darcs.Test.Misc.Encoding+ Darcs.Test.Repository.Inventory Darcs.Test.Util.TestResult Darcs.Test.Util.QuickCheck- Storage.Hashed.Test if flag(optimize)- ghc-options: -O2+ ghc-options: -O2 else- ghc-options: -O0+ ghc-options: -O0 if flag(warn-as-error)- ghc-options: -Werror+ ghc-options: -Werror - -- 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-orphans if flag(threaded) ghc-options: -threaded if flag(rts)- ghc-options: -rtsopts+ ghc-options: -rtsopts -- see http://bugs.darcs.net/issue1037 cc-options: -D_REENTRANT - -- if true to work around cabal bug with flag ordering- if true- default-extensions:- BangPatterns- PatternGuards- GADTs+ default-extensions:+ GADTSyntax+ ExistentialQuantification TypeOperators FlexibleContexts FlexibleInstances@@ -650,5 +604,3 @@ RankNTypes TypeFamilies NoMonoLocalBinds--
@@ -27,27 +27,24 @@ module Main ( main ) where +import Prelude ()+import Darcs.Prelude+ import Control.Exception ( AssertionFailed(..), handle )-import Control.Monad ( forM_ )-import System.IO ( stdin, stdout, stderr, hSetBinaryMode ) import System.Exit ( exitWith, ExitCode(..) ) import System.Environment ( getArgs ) import Darcs.UI.RunCommand ( runTheCommand ) import Darcs.UI.Commands.Help ( helpCmd, listAvailableCommands, printVersion, commandControlList )-import Darcs.UI.Flags ( DarcsFlag(Verbose) ) import Darcs.Util.AtExit ( withAtexit, atexit ) import Darcs.Repository( reportBadSources ) import Darcs.Util.SignalHandler ( withSignalsHandled ) -import Darcs.Util.ByteString ( decodeString ) import Darcs.UI.External ( setDarcsEncodings ) import Darcs.Util.Exec ( ExecException(..) ) import Darcs.Util.Path ( getCurrentDirectory )-import Version ( version, context, builddeps )--#include "impossible.h"+import Version ( version, context, weakhash ) execExceptionHandler :: ExecException -> IO a execExceptionHandler (ExecException cmd args redirects reason) = do@@ -62,7 +59,7 @@ main = withAtexit . withSignalsHandled . handleExecFail . handleAssertFail $ do atexit reportBadSources setDarcsEncodings- argv <- getArgs >>= mapM decodeString+ argv <- getArgs here <- getCurrentDirectory let runHelpCmd = helpCmd (here, here) [] [] -- Explicitly handle no-args and special "help" arguments.@@ -70,20 +67,17 @@ [] -> printVersion >> runHelpCmd ["-h"] -> runHelpCmd ["--help"] -> runHelpCmd- ["--overview"] -> helpCmd (here, here) [Verbose] [] ["--commands"] -> listAvailableCommands ["-v"] -> putStrLn version+ ["-V"] -> putStrLn version ["--version"] -> putStrLn version ["--exact-version"] -> printExactVersion- _ -> do- forM_ [stdout, stdin, stderr] $ \h -> hSetBinaryMode h True- runTheCommand commandControlList (head argv) (tail argv)+ _ -> runTheCommand commandControlList (head argv) (tail argv) where handleExecFail = handle execExceptionHandler handleAssertFail = handle $ \(AssertionFailed e) -> bug e printExactVersion = do- putStrLn $ "darcs compiled on " ++ __DATE__ ++ ", at " ++ __TIME__+ putStrLn $ "darcs compiled on " ++ __DATE__ ++ ", at " ++ __TIME__ ++ "\n"+ putStrLn $ "Weak Hash: " ++ weakhash putStrLn context- putStrLn "Compiled with:\n"- putStr builddeps
@@ -21,15 +21,17 @@ -- These tests check whether the emails generated by darcs meet a few criteria. -- We check for line length and non-ASCII characters. We apparently do not have -- to check for CR-LF newlines because that's handled by sendmail.+ module Darcs.Test.Email ( testSuite ) where import Data.Char ( isPrint )-import qualified Data.ByteString as B ( length, unpack, null, head,+import qualified Data.ByteString as B ( length, unpack, null, head, pack, cons, empty, foldr, ByteString )-import qualified Data.ByteString.Char8 as BC ( unpack )+import qualified Data.ByteString.Char8 as BC ( unlines ) import Test.Framework ( Test, testGroup ) import Test.Framework.Providers.QuickCheck2 ( testProperty )-import Darcs.Util.Printer ( text, renderPS, RenderMode(..) )-import Darcs.UI.Email ( makeEmail, readEmail, formatHeader )+import Test.QuickCheck ( Arbitrary(..) )+import Darcs.Util.Printer ( text, renderPS, packedString )+import Darcs.UI.Email ( makeEmail, readEmail, formatHeader, prop_qp_roundtrip ) testSuite :: Test testSuite = testGroup "Darcs.Email"@@ -38,16 +40,17 @@ , emailHeaderAsciiChars , emailHeaderLinesStart , emailHeaderNoEmptyLines+ , emailCodecRoundtrip ] -- | Checks that darcs can read the emails it generates emailParsing :: Test-emailParsing = testProperty "Checking that email can be parsed" $ \s ->- unlines ("":s++["", ""]) ==- BC.unpack (readEmail (renderPS Standard+emailParsing = testProperty "Checking that email can be parsed" $ \bs ->+ BC.unlines (B.empty:bs++[B.empty,B.empty]) ==+ readEmail (renderPS $ makeEmail "reponame" [] (Just (text "contents\n")) Nothing- (text $ unlines s) (Just "filename")))+ (packedString $ BC.unlines bs) (Just "filename")) -- | Check that formatHeader never creates lines longer than 78 characters -- (excluding the carriage return and line feed)@@ -81,6 +84,13 @@ let headerLines = bsLines (formatHeader cleanField value) cleanField = cleanFieldString field in all (not . B.null) headerLines --(not . B.null . B.filter (not . (`elem` [10, 32, 9]))) headerLines++instance Arbitrary B.ByteString where+ arbitrary = fmap B.pack arbitrary++emailCodecRoundtrip :: Test+emailCodecRoundtrip =+ testProperty "Checking that quoted printable en- then decoding is id" $ prop_qp_roundtrip bsLines :: B.ByteString -> [B.ByteString] bsLines = finalizeFold . B.foldr splitAtLines (B.empty, [])
@@ -0,0 +1,501 @@+{-# LANGUAGE ScopedTypeVariables, FlexibleInstances #-}+module Darcs.Test.HashedStorage( tests ) where++import Prelude hiding ( filter, readFile, writeFile, lookup, (<$>) )+import qualified Prelude+import qualified Data.ByteString.Lazy.Char8 as BLC+import qualified Data.ByteString.Char8 as BC+import System.Directory( doesFileExist, removeFile, doesDirectoryExist )+import System.FilePath( (</>) )+import Control.Monad.Identity+import Control.Monad.Trans( lift )+import Control.Applicative( (<$>) )+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.Lock ( withTempDir )+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 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, BLC.ByteString)]+blobs = [ (floatPath "foo_a", BLC.pack "a\n")+ , (floatPath "foo_dir/foo_a", BLC.pack "a\n")+ , (floatPath "foo_dir/foo_b", BLC.pack "b\n")+ , (floatPath "foo_dir/foo_subdir/foo_a", BLC.pack "a\n")+ , (floatPath "foo space/foo\nnewline", BLC.pack "newline\n")+ , (floatPath "foo space/foo\\backslash", BLC.pack "backslash\n")+ , (floatPath "foo space/foo_a", BLC.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 $ BLC.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 "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 (BLC.pack x)) (sha256 $ BLC.pack x)+ name = makeName+ 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 (BLC.pack "bar")+ assertEqual "new version" y (BLC.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 (BLC.pack "bar")+ assertEqual "old bar/foo" bar_foo (BLC.pack "bar")+ assertEqual "new foo" foo' (BLC.pack "bar")+ assertEqual "new bar/foo" bar_foo' (BLC.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)+ BLC.unpack foo_work_c @?= "b\n"+ BLC.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 $ BLC.unpack file @?= ""+ lift $ BLC.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")++----------------------------------+-- Arbitrary instances+--++instance Arbitrary BLC.ByteString where+ arbitrary = BLC.pack `fmap` arbitrary++instance Arbitrary Hash where+ arbitrary = sized hash'+ where hash' 0 = return NoHash+ hash' _ = SHA256 . BC.pack <$> sequence [ arbitrary | _ <- [1..32] :: [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 (x:_)) _ | x == makeName "_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 <- BC.readFile h_inventory+ let thelines = BC.split '\n' inv+ case thelines of+ [] -> return emptyTree+ (pris_line:_) -> do+ let thehash = decodeDarcsHash $ BC.drop 9 pris_line+ thesize = decodeDarcsSize $ BC.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 . BLC.fromStrict <$> BC.readFile "harness/hstestdata.zip"+ withTempDir "_test_playground" $ \_ -> do+ extractFilesFromArchive [] zipFile+ action
@@ -15,33 +15,45 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.Test.Misc ( testSuite ) where -import Darcs.Util.ByteString ( unpackPSFromUTF8, fromHex2PS, fromPS2Hex )+import Darcs.Util.ByteString+ ( unpackPSFromUTF8, fromHex2PS, fromPS2Hex+ , propHexConversion+ , prop_unlinesPS_linesPS_left_inverse+ , prop_linesPS_length+ , prop_unlinesPS_length+ , spec_betweenLinesPS+ , betweenLinesPS+ ) import Darcs.Util.Diff.Myers ( shiftBoundaries ) -import qualified Data.ByteString.Char8 as BC ( unpack, pack )-import qualified Data.ByteString as B ( concat, empty )+import Darcs.Test.Misc.CommandLine ( commandLineTestSuite )+import qualified Darcs.Test.Misc.Encoding as Encoding++import qualified Data.ByteString.Char8 as BC ( unpack, pack, last )+import qualified Data.ByteString as B ( ByteString, pack, empty, null )+import Data.Char ( ord ) import Data.Array.Base import Control.Monad.ST import Test.HUnit ( assertBool, assertEqual, assertFailure ) import Test.Framework.Providers.QuickCheck2 ( testProperty ) import Test.Framework.Providers.HUnit ( testCase ) import Test.Framework ( Test, testGroup )+import Test.QuickCheck testSuite :: Test testSuite = testGroup "" [ byteStringUtilsTestSuite , lcsTestSuite+ , commandLineTestSuite+ , Encoding.testSuite ] -- ---------------------------------------------------------------------- -- * Darcs.Util.ByteString--- Here are a few quick tests of the shiftBoundaries function. -- ---------------------------------------------------------------------- byteStringUtilsTestSuite :: Test@@ -51,16 +63,27 @@ , testCase "Checking that hex packing and unpacking preserves 'hello world'" (assertEqual "" (BC.unpack (fromHex2PS $ fromPS2Hex $ BC.pack "hello world")) "hello world")- , testProperty "Checking that B.concat works" propConcatPS , testProperty "Checking that hex conversion works" propHexConversion+ , testProperty "unlinesPS is left inverse of linesPS" prop_unlinesPS_linesPS_left_inverse+ , testProperty "linesPS length property" prop_linesPS_length+ , testProperty "unlinesPS length property" prop_unlinesPS_length+ , testProperty "betweenLinesPS behaves like its spec" prop_betweenLinesPS ] -propHexConversion :: String -> Bool-propHexConversion s =- fromHex2PS (fromPS2Hex $ BC.pack s) == BC.pack s+-- tweak the probabilities in favor of newline characters+instance Arbitrary B.ByteString where+ arbitrary = fmap B.pack $ listOf $ frequency+ [ (1, return (fromIntegral (ord '\n')))+ , (4, arbitrary)+ ] -propConcatPS :: [String] -> Bool-propConcatPS ss = concat ss == BC.unpack (B.concat $ map BC.pack ss)+-- betweenLinesPS and spec_betweenLinesPS are equivalent only+-- if certain conditions are met+prop_betweenLinesPS :: B.ByteString -> B.ByteString -> B.ByteString -> Property+prop_betweenLinesPS start end ps =+ not (B.null start) && not (B.null end) &&+ (B.null ps || BC.last ps == '\n') ==>+ betweenLinesPS start end ps == spec_betweenLinesPS start end ps -- ---------------------------------------------------------------------- -- * LCS
@@ -0,0 +1,41 @@+module Darcs.Test.Misc.CommandLine+ (+ commandLineTestSuite+ ) where++import Test.HUnit ( assertEqual, assertFailure )+import Test.Framework.Providers.HUnit ( testCase )+import Test.Framework ( Test, testGroup )++import Darcs.Util.CommandLine ( parseCmd )++formatTable :: [(Char, String)]+formatTable = [('s',"<insert subject here>"),+ ('a',"<insert author here>"),+ ('d',"date")+ ]++testParser :: (String, ([String], Bool)) -> Test+testParser (s, ok) =+ testCase ("Parse: " ++ show s) $+ case parseCmd formatTable s of+ Left e -> assertFailure $ "Parser failed with: " ++ show e+ Right res -> assertEqual ("Parsing: " ++ show s) ok res++testCases :: [(String, ([String], Bool))]+testCases = [("a b",(["a","b"], False)),+ ("a b %<",(["a","b"], True)),+ ("a b %< ",(["a","b"], True)),+ ("\"arg0 contains spaces \\\"quotes\\\"\" b",+ (["arg0 contains spaces \"quotes\"","b"],False)),+ ("a %s %<",(["a","<insert subject here>"], True)),+ ("\"%d\"", (["date"], False)),+ ("\"d %d\"", (["d date"], False)),+ ("\\\a", (["\\\a"], False)),+ ("\"\\\a\"", (["\a"], False)),+ ("\"/foo:%d\"", (["/foo:date"], False))+ ]++commandLineTestSuite :: Test+commandLineTestSuite =+ testGroup "Darcs.Util.CommandLine" $ map testParser testCases
@@ -0,0 +1,36 @@+module Darcs.Test.Misc.Encoding ( testSuite ) where++import qualified Data.ByteString as B+import Control.Monad+import Data.Word+import System.IO.Unsafe++import Darcs.Util.Encoding++import Test.Framework ( Test, testGroup )+import Test.Framework.Providers.QuickCheck2 ( testProperty )+import Test.QuickCheck++decodeThenEncode :: B.ByteString -> B.ByteString+decodeThenEncode = unsafePerformIO . (decode >=> encode)++testSuite :: Test+testSuite = testGroup "Darcs.Util.Encoding"+ [ testProperty "decode then encode roundtrips" propDecodeThenEncodeRoundTrip+ ]++-- could use the bytestring-arbitrary package,+-- but the shrinking isn't as effective as 'shrinkList shrink'+newtype MyByteString = MBS { _mbsBytes :: [Word8] }+ deriving Show++instance Arbitrary MyByteString where+ arbitrary = MBS <$> sized (\n -> vectorOf (100*n) arbitrary)+ shrink (MBS ws) = MBS <$> shrinkList shrink ws++toBS :: MyByteString -> B.ByteString+toBS (MBS ws) = B.pack ws++propDecodeThenEncodeRoundTrip :: MyByteString -> Bool+propDecodeThenEncodeRoundTrip mbs =+ let bstr = toBS mbs in decodeThenEncode bstr == bstr
@@ -1,8 +1,4 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP, ImpredicativeTypes #-}-#if __GLASGOW_HASKELL__ >= 708-{-# LANGUAGE AllowAmbiguousTypes #-}-#endif+{-# LANGUAGE AllowAmbiguousTypes #-} -- Copyright (C) 2002-2005,2007 David Roundy -- -- This program is free software; you can redistribute it and/or modify@@ -35,19 +31,21 @@ import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Witnesses.Sealed-import Darcs.Patch.Witnesses.Eq ( MyEq, unsafeCompare )+import Darcs.Patch.Witnesses.Eq ( Eq2, unsafeCompare ) import Darcs.Patch.Witnesses.Show-import Darcs.Patch.Prim( PrimPatch, coalesce, FromPrim, PrimOf, PrimPatchBase )-import qualified Darcs.Patch.Prim.V1 as V1 ( Prim )+import Darcs.Patch.Prim( PrimPatch, coalesce, FromPrim, PrimOf ) 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 ( RepoPatchV1 )+import qualified Darcs.Patch.V1.Prim as V1 ( Prim )+import qualified Darcs.Patch.V2.Prim as V2 ( Prim ) import Darcs.Patch.V2.RepoPatch ( isConsistent, isForward, RepoPatchV2 )-import Darcs.Patch.Patchy ( Commute(..), Patchy )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Invert ( Invert ) import Darcs.Patch.Merge( Merge ) import Darcs.Patch.Show ( ShowPatchBasic )-import Darcs.Patch.Apply( ApplyState )+import Darcs.Patch.Apply( Apply, ApplyState ) import Darcs.Test.Patch.Arbitrary.Generic import qualified Darcs.Test.Patch.Arbitrary.PrimV1 as P1@@ -76,15 +74,16 @@ import qualified Darcs.Test.Patch.WSub as WSub -type instance ModelOf (FL prim) = ModelOf prim+type Prim1 = V1.Prim+type Prim2 = V2.Prim -type TestGenerator thing gen = (forall t ctx . ((forall wXx wYy . thing wXx wYy -> t) -> (gen ctx -> t)))-type TestCondition thing = (forall wYy wZz . thing wYy wZz -> Bool)-type TestCheck thing t = (forall wYy wZz . thing wYy wZz -> t)+newtype TestGenerator thing gen = TestGenerator (forall t ctx . ((forall wXx wYy . thing wXx wYy -> t) -> (gen ctx -> t)))+newtype TestCondition thing = TestCondition (forall wYy wZz . thing wYy wZz -> Bool)+newtype TestCheck thing t = TestCheck (forall wYy wZz . thing wYy wZz -> t) -- arbitraryThing :: (forall wXx wYy . thing wXx wYy -> t) -> (thing wA wB -> t) arbitraryThing :: x -> TestGenerator thing (thing x)-arbitraryThing _ f p = f p+arbitraryThing _ = TestGenerator (\f p -> f p) -- | Run a test function on a set of data, using HUnit. The test function should -- return @Nothing@ upon success and a @Just x@ upon failure.@@ -123,7 +122,7 @@ , testCases "FL prim recommute" (PropU.recommute WSub.commute) ExU.commutablesFL , testCases "FL prim patch and inverse commute" (PropU.patchAndInverseCommute WSub.commute) ExU.commutablesFL , 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 "fails" (PropU.commuteFails WSub.commute) ([] :: [(Prim2 WSub.:> Prim2) wX wY]) , testCases "read and show work on Prim" PropU.show_read ExU.primPatches , testCases "read and show work on RepoPatchV2" PropU.show_read ExU.repov2Patches , testCases "example flattenings work" PropU.consistentTreeFlattenings ExU.repov2PatchLoopExamples@@ -140,27 +139,16 @@ , testCases "V2 partial permutivity" (PropU.partialPermutivity WSub.commute) ExU.repov2NonduplicateTriples ] -instance PrimPatch prim => Check (RepoPatchV2 prim) where- checkPatch p = return $ isNothing $ isConsistent p--instance Check FileUUID.Prim where- checkPatch _ = return True -- XXX--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 , model ~ ModelOf prim, RepoModel model , RepoState model ~ ApplyState (PrimOf prim) , Show1 (ModelOf prim)- , Check prim, PrimPatchBase prim, PrimOf prim ~ prim+ , Check prim, PrimOf prim ~ prim , FromPrim prim , MightBeEmptyHunk prim , MightHaveDuplicate prim , Show1 (prim wA)- , Show1 ((prim :> prim) wA)- , Show1 (WithState model prim wA) , Arbitrary (Sealed ((prim :> prim) wA)) , Arbitrary (Sealed ((prim :> prim :> prim) wA)) , Arbitrary (Sealed (prim wA))@@ -195,12 +183,18 @@ ] where arbitraryThing' = arbitraryThing (undefined :: wA) -- bind the witness for generator +consistentV2 :: RepoPatchV2 Prim2 wX wY -> TestResult+consistentV2 = fromMaybe . isConsistent++commuteRepoPatchV2s :: (RepoPatchV2 Prim2 :> RepoPatchV2 Prim2) wX wY -> Maybe ((RepoPatchV2 Prim2 :> RepoPatchV2 Prim2) wX wY)+commuteRepoPatchV2s = commute+ qc_V2P1 :: [Test] qc_V2P1 = [ testProperty "tree flattenings are consistent... "- (PropR.propConsistentTreeFlattenings :: Sealed (WithStartState (ModelOf V1.Prim) (Tree V1.Prim)) -> Bool)+ (PropR.propConsistentTreeFlattenings :: Sealed (WithStartState (ModelOf Prim2) (Tree Prim2)) -> Bool) , testProperty "with quickcheck that RepoPatchV2 patches are consistent... "- (unseal $ P1.patchFromTree $ fromMaybe . isConsistent)+ (unseal $ P1.patchFromTree $ consistentV2) -- permutivity ---------------------------------------------------------------------------- , testConditional "permutivity" (unseal $ P1.commuteTripleFromTree notDuplicatestriple)@@ -214,7 +208,7 @@ ] qc_V2 :: forall prim wXx wYy . (PrimPatch prim, Show1 (ModelOf prim), RepoModel (ModelOf prim),- Check (RepoPatchV2 prim), ArbitraryPrim prim, Show2 prim,+ ArbitraryPrim prim, Show2 prim, RepoState (ModelOf prim) ~ ApplyState prim) => prim wXx wYy -> [Test] qc_V2 _ =@@ -228,11 +222,11 @@ -> Maybe (Tree (RepoPatchV2 prim) wX))) ] ++ concat- [ 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+ [ merge_properties (undefined :: RepoPatchV2 prim wX wY) "tree" (TestGenerator mergePairFromTree)+ , merge_properties (undefined :: RepoPatchV2 prim wX wY) "twfp" (TestGenerator mergePairFromTWFP)+ , pair_properties (undefined :: RepoPatchV2 prim wX wY) "tree" (TestGenerator commutePairFromTree)+ , pair_properties (undefined :: RepoPatchV2 prim wX wY) "twfp" (TestGenerator commutePairFromTWFP)+ , patch_properties (undefined :: RepoPatchV2 prim wX wY) "tree" (TestGenerator patchFromTree) ] properties :: forall thing gen. (Show1 gen, Arbitrary (Sealed gen)) =>@@ -241,86 +235,98 @@ -> String -> String -> forall t. Testable t => [(String, TestCondition thing, TestCheck thing t)] -> [Test]-properties gen prefix genname tests =+properties (TestGenerator gen) prefix genname tests = [ cond name condition check | (name, condition, check) <- tests ] where cond :: forall testable. Testable testable => String -> TestCondition thing -> TestCheck thing testable -> Test- cond t c p =+ cond t (TestCondition c) (TestCheck p) = testConditional (prefix ++ " (" ++ genname ++ "): " ++ t) (unseal $ gen c) (unseal $ gen p) type PropList what gen = String -> TestGenerator what gen -> [Test] pair_properties :: forall p gen x y- . ( Show1 gen, Arbitrary (Sealed gen), Patchy p, MightHaveDuplicate p- , ShowPatchBasic p, MyEq p+ . ( Show1 gen, Arbitrary (Sealed gen), MightHaveDuplicate p+ , Commute p, Invert p, ShowPatchBasic p, Eq2 p ) => p x y -> PropList (p :> p) gen pair_properties _ genname gen = properties gen "commute" genname- [ ("recommute" , const True , PropG.recommute commute )- , ("nontrivial recommute" , nontrivialCommute, PropG.recommute commute )- , ("inverses commute" , const True , PropG.commuteInverses commute )- , ("nontrivial inverses" , nontrivialCommute, PropG.commuteInverses commute )- , ("inverse composition" , const True , PropG.inverseComposition )+ [ ("recommute" , TestCondition (const True) , TestCheck (PropG.recommute commute) )+ , ("nontrivial recommute" , TestCondition nontrivialCommute, TestCheck (PropG.recommute commute) )+ , ("inverses commute" , TestCondition (const True) , TestCheck (PropG.commuteInverses commute) )+ , ("nontrivial inverses" , TestCondition nontrivialCommute, TestCheck (PropG.commuteInverses commute) )+ , ("inverse composition" , TestCondition (const True) , TestCheck PropG.inverseComposition ) ] coalesce_properties :: forall p gen x y- . ( Show1 gen, Arbitrary (Sealed gen), Patchy p, PrimPatch p+ . ( Show1 gen, Arbitrary (Sealed gen), PrimPatch p , ArbitraryPrim p, MightBeEmptyHunk p ) => p x y -> PropList (p :> p :> p) gen coalesce_properties p genname gen = properties gen "commute" genname- (if runCoalesceTests p then [ ("coalesce commutes with commute", const True, PropG.coalesceCommute coalesce) ] else [])+ (if runCoalesceTests p then [ ("coalesce commutes with commute", TestCondition (const True), TestCheck (PropG.coalesceCommute coalesce)) ] else []) -- The following properties do not hold for "RepoPatchV2" patches (conflictors and -- duplicates, specifically) . nonrpv2_commute_properties :: forall p gen x y- . (Show1 gen, Arbitrary (Sealed gen), Patchy p, ShowPatchBasic p, MyEq p)+ . (Show1 gen, Arbitrary (Sealed gen), Commute p, Invert p, ShowPatchBasic p, Eq2 p) => p x y -> PropList (p :> p) 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)+ [ ("patch & inverse commute", TestCondition (const True) , TestCheck (PropG.patchAndInverseCommute commute))+ , ("patch & inverse commute", TestCondition nontrivialCommute, TestCheck (PropG.patchAndInverseCommute commute)) ] -patch_properties :: forall p gen x y. (Show1 gen, Arbitrary (Sealed gen), Patchy p, MyEq p)+patch_properties :: forall p gen x y. (Show1 gen, Arbitrary (Sealed gen), Invert p, Apply p, Eq2 p) => p x y -> PropList p gen patch_properties _ genname gen = properties gen "patch" genname- [ ("inverse . inverse is id" , const True , PropG.invertSymmetry)+ [ ("inverse . inverse is id" , TestCondition (const True) , TestCheck PropG.invertSymmetry) ] patch_repo_properties :: forall p gen x y. (Show1 gen, Arbitrary (Sealed gen),- Patchy p, ShowPatchBasic p,+ Invert p, Apply p, ShowPatchBasic p, RepoModel (ModelOf (PrimOf p)), RepoState (ModelOf (PrimOf p)) ~ ApplyState p) => p x y -> PropList (WithState (ModelOf (PrimOf p)) p) gen patch_repo_properties _ genname gen = properties gen "patch/repo" genname- [ ("invert rollback" , const True , PropG.invertRollback)+ [ ("invert rollback" , TestCondition (const True) , TestCheck PropG.invertRollback) ] -pair_repo_properties :: forall p gen x y. (Show1 gen, Arbitrary (Sealed gen), Patchy p,- MightBeEmptyHunk p,- RepoModel (ModelOf p),- RepoState (ModelOf p) ~ ApplyState p)- => p x y -> PropList (WithState (ModelOf p) (p :> p)) gen+pair_repo_properties+ :: forall p gen x y.+ ( Show1 gen+ , Arbitrary (Sealed gen)+ , Commute p+ , Apply p+ , ShowPatchBasic p+ , MightBeEmptyHunk p+ , RepoModel (ModelOf p)+ , RepoState (ModelOf p) ~ ApplyState p+ )+ => p x y -> PropList (WithState (ModelOf p) (p :> p)) gen pair_repo_properties _ genname gen =- properties gen "patch/repo" genname- [ ("commute is effect preserving" , const True , PropG.effectPreserving commute )- ]+ properties+ gen+ "patch/repo"+ genname+ [ ( "commute is effect preserving"+ , TestCondition (const True)+ , TestCheck (PropG.effectPreserving commute))+ ] merge_properties :: forall p gen x y. (Show1 gen, Arbitrary (Sealed gen)- , Patchy p, MyEq p, Merge p, ShowPatchBasic p+ , Invert p, Eq2 p, Merge p, ShowPatchBasic p , MightHaveDuplicate p, Show2 p, Check p) => p x y -> PropList (p :\/: p) gen merge_properties _ genname gen = properties gen "merge" genname- [ ("merge either way" , const True , PropG.mergeEitherWay )- , ("merge either way valid" , const True , Prop1.tMergeEitherWayValid)- , ("nontrivial merge either way", nontrivialMerge, PropG.mergeEitherWay )- , ("merge commute" , const True , PropG.mergeCommute )+ [ ("merge either way" , TestCondition (const True) , TestCheck PropG.mergeEitherWay )+ , ("merge either way valid" , TestCondition (const True) , TestCheck Prop1.tMergeEitherWayValid)+ , ("nontrivial merge either way", TestCondition nontrivialMerge, TestCheck PropG.mergeEitherWay )+ , ("merge commute" , TestCondition (const True) , TestCheck PropG.mergeCommute ) ] qc_V1P1 :: [Test]@@ -357,10 +363,10 @@ ] -- 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.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 -}+ {- ++ merge_properties (undefined :: V1.RepoPatchV1 Prim1 wX wY) "tree" mergePairFromTree+ ++ merge_properties (undefined :: V1.RepoPatchV1 Prim1 wX wY) "twfp" mergePairFromTWFP+ ++ commute_properties (undefined :: V1.RepoPatchV1 Prim1 wX wY) "tree" commutePairFromTree+ ++ commute_properties (undefined :: V1.RepoPatchV1 Prim1 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 rt p -> [Test]@@ -370,16 +376,22 @@ -- | This is the big list of tests that will be run using testrunner. 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 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 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- ]+testSuite =+ [ testGroup "Darcs.Patch.Prim.V1 for V1" $ qc_prim (undefined :: Prim1 wX wY)+ -- testing both Prim1 and Prim2 here is redundant, since they differ+ -- only in their read/show behavior, which is not tested in qc_prim;+ -- we still include them because such tests might be added in the future+ , testGroup "Darcs.Patch.Prim.V1 for V2" $ qc_prim (undefined :: Prim2 wX wY)+ , testGroup "Darcs.Patch.Prim.FileUUID" $ qc_prim (undefined :: FileUUID.Prim wX wY)+ , testGroup "Darcs.Patch.V1 (using Prim.V1)" $+ unit_V1P1 ++ qc_V1P1 +++ general_patchTests (PatchType :: PatchType rt (V1.RepoPatchV1 Prim1))+ , testGroup "Darcs.Patch.V2 (using Prim.V1)" $+ unit_V2P1 ++ qc_V2 (undefined :: Prim2 wX wY) ++ qc_V2P1 +++ general_patchTests (PatchType :: PatchType rt (RepoPatchV2 Prim2))+ , 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+ ]
@@ -1,7 +1,5 @@-{-# OPTIONS_GHC -fno-warn-deprecations -fno-warn-orphans #-}-{-# LANGUAGE CPP, UndecidableInstances, ScopedTypeVariables, MultiParamTypeClasses,+{-# LANGUAGE UndecidableInstances, ScopedTypeVariables, MultiParamTypeClasses, FlexibleInstances, ViewPatterns #-}- module Darcs.Test.Patch.Arbitrary.Generic ( Tree(..), TreeWithFlattenPos(..), G2(..), ArbitraryPrim(..), NullPatch(..), RepoModel(..) , MightBeEmptyHunk(..), MightHaveDuplicate(..)@@ -24,9 +22,9 @@ import Darcs.Patch.Witnesses.Unsafe import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Merge ( Merge(..) )-import Darcs.Patch.Patchy ( Invert(..), Commute(..) )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Commute ( Commute(..) ) import Darcs.Patch.Prim ( PrimOf, PrimPatch, PrimPatchBase, FromPrim(..), PrimConstruct( anIdentity ) )-import Darcs.Patch.Prim.V1 () import Darcs.Patch.V2 ( RepoPatchV2 ) -- XXX this is more or less a hack --import Darcs.ColorPrinter ( errorDoc ) --import Darcs.ColorPrinter ( traceDoc )
@@ -1,8 +1,9 @@--- TODO: Remove these warning disabling flags...-{-# OPTIONS_GHC -w #-}-{-# LANGUAGE CPP, MultiParamTypeClasses, OverloadedStrings #-}+{-# LANGUAGE MultiParamTypeClasses, OverloadedStrings #-} module Darcs.Test.Patch.Arbitrary.PrimFileUUID where +import Prelude ()+import Darcs.Prelude+ import qualified Darcs.Test.Patch.Arbitrary.Generic as T ( commuteTripleFromTree, commutePairFromTree, commutePairFromTWFP , mergePairFromTree, mergePairFromTWFP@@ -18,24 +19,20 @@ 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.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.Test.Util.QuickCheck ( 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 qualified Data.ByteString as B 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 @@ -59,18 +56,15 @@ runCoalesceTests _ = False hasPrimConstruct _ = False -hunkIdentity (Hunk _ old new) | old == new = unsafeCoerceP IsEq-hunkIdentity _ = NotEq+instance MightBeEmptyHunk Prim+instance MightHaveDuplicate Prim instance NullPatch Prim where- nullPatch (BinaryHunk _ x) = hunkIdentity x- nullPatch (TextHunk _ x) = hunkIdentity x+ nullPatch Identity = IsEq+ nullPatch (Hunk _ (H _ old new))+ | old == new = unsafeCoerceP IsEq 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 @@ -81,93 +75,83 @@ ---------------------------------------------------------------------- -- * 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+aHunk :: B.ByteString -> Gen (Hunk wX wY)+aHunk content = do+ pos <- choose (0, B.length content)+ oldLen <- choose (0, B.length content - pos)+ new <- scale (`div` 8) aContent+ let old = B.take oldLen $ B.drop pos $ content+ return $ H pos old new -aTextHunk :: forall wX wY . (UUID, Object Fail) -> Gen (Prim wX wY)+aTextHunk :: (UUID, Object Fail) -> Gen (Prim wX wY) aTextHunk (uuid, (Blob text _)) =- do hunk <- aHunk (unFail text)- return $ TextHunk uuid hunk+ do h <- aHunk (unFail text)+ return $ Hunk uuid h+aTextHunk _ = impossible -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+aManifest :: UUID -> (UUID, Object Fail) -> Gen (Prim wX wY)+aManifest uuid (dirId, Directory dir) =+ do filename <- aFilename `notIn` (M.keys dir)+ return $ Manifest uuid (L dirId filename)+aManifest _ _ = impossible -aDemanifest :: forall wX wY . UUID -> Location -> Gen (Prim wX wY)+aDemanifest :: 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 :: 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+ = do mbFile <- maybeOf repoFiles -- some file, not necessarily manifested+ dir <- elements repoDirs -- some directory, not necessarily manifested+ -- note, the root directory always exists and is never manifested nor demanifested+ mbDemanifested <- maybeOf notManifested -- something manifested+ mbManifested <- maybeOf manifested -- something not manifested+ fresh <- anUUID `notIn` repoIds repo -- a fresh uuid+ let whenjust m x = if isJust m then x else 0+ whenfile = whenjust mbFile+ whendemanifested = whenjust mbDemanifested+ whenmanifested = whenjust mbManifested 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+ [ ( whenfile 12, aTextHunk $ fromJust mbFile ) -- edit an existing file+ , ( 2, aTextHunk (fresh, Blob (return "") NoHash) ) -- edit a new file+ , ( whendemanifested 2 -- manifest an existing object+ , aManifest (fromJust mbDemanifested) dir+ )+ , ( whenmanifested 2+ , uncurry aDemanifest $ fromJust mbManifested+ ) ] 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)+ manifested = [ (uuid, (L dirid name)) | (dirid, Directory dir) <- repoDirs+ , (name, uuid) <- M.toList dir ]+ notManifested = [ uuid | (uuid, _) <- nonRootObjects+ , not (uuid `elem` map fst manifested) ]+ repoFiles = [ (uuid, Blob x y) | (uuid, Blob x y) <- repoObjects repo ]+ repoDirs = [ (uuid, Directory x) | (uuid, Directory x) <- repoObjects repo ]+ nonRootObjects = filter notRoot $ repoObjects repo where+ notRoot (uuid, _) = uuid == rootId ---------------------------------------------------------------------- -- *** 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, Object Fail) -> Gen ((Prim :> Prim) wX wY) hunkPair (uuid, (Blob file _)) =- do h1@(Hunk l1 old1 new1) <- aHunk (unFail file)+ do h1@(H off1 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))+ H off2' old2 new2 <- aHunk content'+ let off2 = off2' + delta+ return (Hunk uuid (H off1 old1 new1) :> Hunk uuid (H off2 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+ selectChunk (H off old new) content = elements [prefix, suffix]+ where prefix = (0, B.take off content)+ suffix = (off + B.length new, B.drop (off + B.length old) content)+hunkPair _ = impossible -aPrimPair :: forall wX wY . FileUUIDModel wX -> Gen (WithEndState FileUUIDModel ((Prim :> Prim) wX) wY)+aPrimPair :: FileUUIDModel wX+ -> Gen (WithEndState FileUUIDModel ((Prim :> Prim) wX) wY) aPrimPair repo = do mbFile <- maybeOf repoFiles frequency@@ -183,7 +167,7 @@ ) ] where- repoFiles = [ (id, Blob x y) | (id, Blob x y) <- repoObjects repo ]+ repoFiles = [ (uuid, Blob x y) | (uuid, Blob x y) <- repoObjects repo ] ---------------------------------------------------------------------- -- Arbitrary instances@@ -193,6 +177,11 @@ instance ArbitraryState FileUUIDModel Prim where arbitraryState s = seal <$> aPrim s+++instance Arbitrary (Sealed2 (FL (WithState FileUUIDModel Prim))) where+ arbitrary = do repo <- ourSmallRepo+ liftM (unseal (seal2 . wesPatch)) $ arbitraryState repo instance Arbitrary (Sealed2 Prim) where
@@ -1,5 +1,4 @@-{-# LANGUAGE CPP, MultiParamTypeClasses #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE MultiParamTypeClasses #-} module Darcs.Test.Patch.Arbitrary.PrimV1 where import qualified Darcs.Test.Patch.Arbitrary.Generic as T@@ -20,39 +19,44 @@ import Darcs.Patch.Witnesses.Eq import Darcs.Patch.Witnesses.Unsafe import Darcs.Patch.Witnesses.Ordered-import Darcs.Patch.Prim.V1 ()-import Darcs.Patch.Prim.V1.Core ( FilePatchType( Hunk, TokReplace ), Prim( FP ), isIdentity )+import Darcs.Patch.Prim.V1.Core ( FilePatchType( Hunk, TokReplace ), isIdentity )+import qualified Darcs.Patch.Prim.V1.Core as Prim ( Prim( FP ) )+import qualified Darcs.Patch.V1.Prim as V1 ( Prim(..) )+import qualified Darcs.Patch.V2.Prim as V2 ( Prim(..) ) import Darcs.Patch.RepoPatch ( RepoPatch ) import Darcs.Patch.FileHunk( IsHunk( isHunk ), FileHunk(..) ) import Darcs.Test.Patch.V1Model import Darcs.Util.Path+import qualified Darcs.Util.Tree as UT ( Tree ) import Darcs.Test.Util.QuickCheck ( alpha, notIn, maybeOf ) import Darcs.UI.Commands.Replace ( defaultToks ) import Darcs.Patch.Prim+import Darcs.Patch.Apply ( ApplyState ) import qualified Data.ByteString.Char8 as BC import Data.Maybe ( isJust ) -#include "impossible.h"+type Prim1 = V1.Prim+type Prim2 = V2.Prim -patchFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . p wY wZ -> t) -> WithStartState V1Model (Tree Prim) wX -> t+patchFromTree :: (RepoPatch p, PrimOf p ~ prim) => (forall wY wZ . p wY wZ -> t) -> WithStartState V1Model (Tree prim) wX -> t patchFromTree = T.patchFromTree -mergePairFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :\/: p) wY wZ -> t) -> WithStartState V1Model (Tree Prim) wX -> t+mergePairFromTree :: (RepoPatch p, PrimOf p ~ prim) => (forall wY wZ . (p :\/: p) wY wZ -> t) -> WithStartState V1Model (Tree prim) wX -> t mergePairFromTree = T.mergePairFromTree -mergePairFromTWFP :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :\/: p) wY wZ -> t) -> WithStartState V1Model (TreeWithFlattenPos Prim) wX -> t+mergePairFromTWFP :: (RepoPatch p, PrimOf p ~ prim) => (forall wY wZ . (p :\/: p) wY wZ -> t) -> WithStartState V1Model (TreeWithFlattenPos prim) wX -> t mergePairFromTWFP = T.mergePairFromTWFP -commutePairFromTWFP :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :> p) wY wZ -> t) -> WithStartState V1Model (TreeWithFlattenPos Prim) wX -> t+commutePairFromTWFP :: (RepoPatch p, PrimOf p ~ prim) => (forall wY wZ . (p :> p) wY wZ -> t) -> WithStartState V1Model (TreeWithFlattenPos prim) wX -> t commutePairFromTWFP = T.commutePairFromTWFP -commutePairFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :> p) wY wZ -> t) -> WithStartState V1Model (Tree Prim) wX -> t+commutePairFromTree :: (RepoPatch p, PrimOf p ~ prim) => (forall wY wZ . (p :> p) wY wZ -> t) -> WithStartState V1Model (Tree prim) wX -> t commutePairFromTree = T.commutePairFromTree -commuteTripleFromTree :: (RepoPatch p, PrimOf p ~ Prim) => (forall wY wZ . (p :> p :> p) wY wZ -> t) -> WithStartState V1Model (Tree Prim) wX -> t+commuteTripleFromTree :: (RepoPatch p, PrimOf p ~ prim) => (forall wY wZ . (p :> p :> p) wY wZ -> t) -> WithStartState V1Model (Tree prim) wX -> t commuteTripleFromTree = T.commuteTripleFromTree nonEmptyHunk :: (IsHunk p) => p wX wY -> Bool@@ -69,33 +73,49 @@ nonEmptyHunksFLPair :: (IsHunk p) => (FL p :> FL p) wX wY -> Bool nonEmptyHunksFLPair (ps :> qs) = allFL nonEmptyHunk ps && allFL nonEmptyHunk qs -type instance ModelOf Prim = V1Model-instance ArbitraryPrim Prim+type instance ModelOf Prim1 = V1Model+type instance ModelOf Prim2 = V1Model+instance ArbitraryPrim Prim1+instance ArbitraryPrim Prim2 -instance NullPatch Prim where- nullPatch (FP _ fp) = nullPatch fp- nullPatch p | IsEq <- isIdentity p = IsEq+instance NullPatch Prim2 where+ nullPatch (V2.Prim (Prim.FP _ fp)) = nullPatch fp+ nullPatch p | IsEq <- isIdentity (V2.unPrim p) = IsEq nullPatch _ = NotEq +instance NullPatch Prim1 where+ nullPatch (V1.Prim (Prim.FP _ fp)) = nullPatch fp+ nullPatch p | IsEq <- isIdentity (V1.unPrim p) = IsEq+ nullPatch _ = NotEq+ instance NullPatch FilePatchType where nullPatch (Hunk _ [] []) = unsafeCoerceP IsEq -- is this safe? nullPatch _ = NotEq -instance MightBeEmptyHunk Prim where- isEmptyHunk (FP _ (Hunk _ [] [])) = True+instance MightBeEmptyHunk Prim1 where+ isEmptyHunk (V1.Prim (Prim.FP _ (Hunk _ [] []))) = True isEmptyHunk _ = False -instance MightHaveDuplicate Prim+instance MightBeEmptyHunk Prim2 where+ isEmptyHunk (V2.Prim (Prim.FP _ (Hunk _ [] []))) = True+ isEmptyHunk _ = False -instance Arbitrary (Sealed2 (FL (WithState V1Model Prim))) where+instance MightHaveDuplicate Prim1+instance MightHaveDuplicate Prim2++instance Arbitrary (Sealed2 (FL (WithState V1Model Prim1))) where arbitrary = do repo <- ourSmallRepo liftM (unseal (seal2 . wesPatch)) $ arbitraryState repo +instance Arbitrary (Sealed2 (FL (WithState V1Model Prim2))) 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 :: Int -> Tree prim wX -> Bool propFail n xs = sizeTree xs < n ----------------------------------------------------------------------@@ -153,21 +173,21 @@ ---------------------------------------------------------------------- -- ** Prim generators -aHunkP :: forall wX wY . (AnchoredPath,File) -> Gen (Prim wX wY)+aHunkP :: forall prim wX wY . PrimPatch prim => (AnchoredPath,File) -> Gen (prim wX wY) aHunkP (path,file) = do Hunk pos old new <- aHunk content return $ hunk (ap2fp path) pos old new where content = fileContent file -aTokReplaceP :: forall wX wY . (AnchoredPath,File) -> Gen (Prim wX wY)+aTokReplaceP :: forall prim wX wY . PrimPatch prim => (AnchoredPath,File) -> Gen (prim wX wY) aTokReplaceP (path,file) = do TokReplace tokchars old new <- aTokReplace content return $ tokreplace (ap2fp path) tokchars old new where content = fileContent file -anAddFileP :: forall wX wY . (AnchoredPath,Dir) -> Gen (Prim wX wY)+anAddFileP :: forall prim wX wY . PrimPatch prim => (AnchoredPath,Dir) -> Gen (prim wX wY) anAddFileP (path,dir) = do newFilename <- aFilename `notIn` existing let newPath = path `appendPath` newFilename@@ -175,11 +195,11 @@ where existing = map fst $ filterFiles $ dirContent dir -aRmFileP :: forall wX wY . AnchoredPath -- ^ Path of an empty file- -> Prim wX wY+aRmFileP :: forall prim wX wY . PrimPatch prim => AnchoredPath -- ^ Path of an empty file+ -> prim wX wY aRmFileP path = rmfile (ap2fp path) -anAddDirP :: forall wX wY . (AnchoredPath,Dir) -> Gen (Prim wX wY)+anAddDirP :: forall prim wX wY . PrimPatch prim => (AnchoredPath,Dir) -> Gen (prim wX wY) anAddDirP (path,dir) = do newDirname <- aDirname `notIn` existing let newPath = path `appendPath` newDirname@@ -187,11 +207,11 @@ where existing = map fst $ filterDirs $ dirContent dir -aRmDirP :: forall wX wY . AnchoredPath -- ^ Path of an empty directory- -> Prim wX wY+aRmDirP :: forall prim wX wY . PrimPatch prim => AnchoredPath -- ^ Path of an empty directory+ -> prim wX wY aRmDirP path = rmdir (ap2fp path) -aMoveP :: forall wX wY . Gen Name -> AnchoredPath -> (AnchoredPath,Dir) -> Gen (Prim wX wY)+aMoveP :: forall prim wX wY . PrimPatch prim => Gen Name -> AnchoredPath -> (AnchoredPath,Dir) -> Gen (prim wX wY) aMoveP nameGen oldPath (dirPath,dir) = do newName <- nameGen `notIn` existing let newPath = dirPath `appendPath` newName@@ -199,8 +219,9 @@ where existing = map fst $ dirContent dir --- | Generates any type of 'Prim' patch, except binary and setpref patches.-aPrim :: forall wX wY . V1Model wX -> Gen (WithEndState V1Model (Prim wX) wY)+-- | Generates any type of 'prim' patch, except binary and setpref patches.+aPrim :: forall prim wX wY . (PrimPatch prim, ApplyState prim ~ RepoState V1Model)+ => V1Model wX -> Gen (WithEndState V1Model (prim wX) wY) aPrim repo = do mbFile <- maybeOf repoFiles mbEmptyFile <- maybeOf $ filter (isEmpty . snd) repoFiles@@ -266,7 +287,7 @@ -- *** Pairs of primitive patches -- Try to generate commutable pairs of hunks-hunkPairP :: forall wX wY . (AnchoredPath,File) -> Gen ((Prim :> Prim) wX wY)+hunkPairP :: forall prim wX wY . PrimPatch prim => (AnchoredPath,File) -> Gen ((prim :> prim) wX wY) hunkPairP (path,file) = do h1@(Hunk l1 old1 new1) <- aHunk content (delta, content') <- selectChunk h1 content@@ -284,7 +305,7 @@ suffix = (start + length new, drop (start + length old) content_) selectChunk _ _ = impossible -aPrimPair :: forall wX wY . V1Model wX -> Gen (WithEndState V1Model ((Prim :> Prim) wX) wY)+aPrimPair :: forall prim wX wY . (PrimPatch prim, ArbitraryState V1Model prim, ApplyState prim ~ RepoState V1Model) => V1Model wX -> Gen (WithEndState V1Model ((prim :> prim) wX) wY) aPrimPair repo = do mbFile <- maybeOf repoFiles frequency@@ -337,66 +358,143 @@ ourSmallRepo :: Gen (V1Model wX) ourSmallRepo = aSmallRepo -instance ArbitraryState V1Model Prim where+instance ArbitraryState V1Model Prim1 where arbitraryState s = seal <$> aPrim s +instance ArbitraryState V1Model Prim2 where+ arbitraryState s = seal <$> aPrim s -instance Arbitrary (Sealed2 Prim) where++instance Arbitrary (Sealed (Prim1 wA)) where+ arbitrary = makeSGen ourSmallRepo++instance Arbitrary (Sealed (Prim2 wA)) where+ arbitrary = makeSGen ourSmallRepo++instance Arbitrary (Sealed2 Prim1) where arbitrary = makeS2Gen ourSmallRepo -instance Arbitrary (Sealed2 (Prim :> Prim)) where+instance Arbitrary (Sealed2 Prim2) where+ arbitrary = makeS2Gen ourSmallRepo++arbitrarySeal2 :: (PrimPatch prim, ApplyState prim ~ UT.Tree,+ ArbitraryState V1Model prim)+ => Gen (Sealed2 (prim :> prim))+arbitrarySeal2 = do+ repo <- ourSmallRepo+ WithEndState pp _ <- aPrimPair repo+ return $ seal2 pp++arbitrarySeal :: (PrimPatch prim, ApplyState prim ~ UT.Tree,+ ArbitraryState V1Model prim)+ => Gen (Sealed ((:>) prim prim wX))+arbitrarySeal = do+ repo <- ourSmallRepo+ WithEndState pp _ <- aPrimPair repo+ return $ seal pp++instance Arbitrary (Sealed2 (Prim1 :> Prim1)) where+ arbitrary = arbitrarySeal2++instance Arbitrary (Sealed2 (Prim2 :> Prim2)) where+ arbitrary = arbitrarySeal2++instance Arbitrary (Sealed ((Prim1 :> Prim1) wA)) where+ arbitrary = arbitrarySeal++instance Arbitrary (Sealed ((Prim2 :> Prim2) wA)) where+ arbitrary = arbitrarySeal++instance Arbitrary (Sealed2 (Prim1 :> Prim1 :> Prim1)) where+ arbitrary = makeS2Gen ourSmallRepo++instance Arbitrary (Sealed ((Prim1 :> Prim1 :> Prim1) a)) where+ arbitrary = makeSGen ourSmallRepo++instance Arbitrary (Sealed2 (FL Prim1)) where+ arbitrary = makeS2Gen ourSmallRepo++instance Arbitrary (Sealed ((FL Prim1) wA)) where+ arbitrary = makeSGen ourSmallRepo++instance Arbitrary (Sealed2 (FL Prim1 :> FL Prim1)) where+ arbitrary = makeS2Gen ourSmallRepo++instance Arbitrary (Sealed ((FL Prim1 :> FL Prim1) wA)) where+ arbitrary = makeSGen ourSmallRepo++instance Arbitrary (Sealed2 (WithState V1Model Prim1)) where+ arbitrary = makeWS2Gen ourSmallRepo++instance Arbitrary (Sealed (WithState V1Model Prim1 wA)) where+ arbitrary = makeWSGen ourSmallRepo++instance Arbitrary (Sealed (WithState V1Model (FL Prim1) wA)) where+ arbitrary = makeWSGen ourSmallRepo++instance Arbitrary (Sealed2 (WithState V1Model (Prim1 :> Prim1))) where arbitrary = do repo <- ourSmallRepo- WithEndState pp _ <- aPrimPair repo- return $ seal2 pp+ WithEndState pp repo' <- aPrimPair repo+ return $ seal2 $ WithState repo pp repo' -instance Arbitrary (Sealed ((Prim :> Prim) wA)) where+instance Arbitrary (Sealed (WithState V1Model (Prim1 :> Prim1) a)) where arbitrary = do repo <- ourSmallRepo- WithEndState pp _ <- aPrimPair repo- return $ seal pp+ WithEndState pp repo' <- aPrimPair repo+ return $ seal $ WithState repo pp repo' -instance Arbitrary (Sealed2 (Prim :> Prim :> Prim)) where++instance Arbitrary (Sealed2 (WithState V1Model (FL Prim1))) where+ arbitrary = makeWS2Gen ourSmallRepo++instance Arbitrary (Sealed2 (WithState V1Model (FL Prim1 :> FL Prim1))) where+ arbitrary = makeWS2Gen ourSmallRepo++instance Arbitrary (Sealed (WithState V1Model (FL Prim1 :> FL Prim1) a)) where+ arbitrary = makeWSGen ourSmallRepo++instance Arbitrary (Sealed2 (Prim2 :> Prim2 :> Prim2)) where arbitrary = makeS2Gen ourSmallRepo -instance Arbitrary (Sealed ((Prim :> Prim :> Prim) a)) where+instance Arbitrary (Sealed ((Prim2 :> Prim2 :> Prim2) a)) where arbitrary = makeSGen ourSmallRepo -instance Arbitrary (Sealed2 (FL Prim)) where+instance Arbitrary (Sealed2 (FL Prim2)) where arbitrary = makeS2Gen ourSmallRepo -instance Arbitrary (Sealed ((FL Prim) wA)) where+instance Arbitrary (Sealed ((FL Prim2) wA)) where arbitrary = makeSGen ourSmallRepo -instance Arbitrary (Sealed2 (FL Prim :> FL Prim)) where+instance Arbitrary (Sealed2 (FL Prim2 :> FL Prim2)) where arbitrary = makeS2Gen ourSmallRepo -instance Arbitrary (Sealed ((FL Prim :> FL Prim) wA)) where+instance Arbitrary (Sealed ((FL Prim2 :> FL Prim2) wA)) where arbitrary = makeSGen ourSmallRepo -instance Arbitrary (Sealed2 (WithState V1Model Prim)) where+instance Arbitrary (Sealed2 (WithState V1Model Prim2)) where arbitrary = makeWS2Gen ourSmallRepo -instance Arbitrary (Sealed (WithState V1Model Prim wA)) where+instance Arbitrary (Sealed (WithState V1Model Prim2 wA)) where arbitrary = makeWSGen ourSmallRepo -instance Arbitrary (Sealed (WithState V1Model (FL Prim) wA)) where+instance Arbitrary (Sealed (WithState V1Model (FL Prim2) wA)) where arbitrary = makeWSGen ourSmallRepo -instance Arbitrary (Sealed2 (WithState V1Model (Prim :> Prim))) where+instance Arbitrary (Sealed2 (WithState V1Model (Prim2 :> Prim2))) where arbitrary = do repo <- ourSmallRepo WithEndState pp repo' <- aPrimPair repo return $ seal2 $ WithState repo pp repo' -instance Arbitrary (Sealed (WithState V1Model (Prim :> Prim) a)) where+instance Arbitrary (Sealed (WithState V1Model (Prim2 :> Prim2) a)) where arbitrary = do repo <- ourSmallRepo WithEndState pp repo' <- aPrimPair repo return $ seal $ WithState repo pp repo' -instance Arbitrary (Sealed2 (WithState V1Model (FL Prim))) where+instance Arbitrary (Sealed2 (WithState V1Model (FL Prim2))) where arbitrary = makeWS2Gen ourSmallRepo -instance Arbitrary (Sealed2 (WithState V1Model (FL Prim :> FL Prim))) where+instance Arbitrary (Sealed2 (WithState V1Model (FL Prim2 :> FL Prim2))) where arbitrary = makeWS2Gen ourSmallRepo -instance Arbitrary (Sealed (WithState V1Model (FL Prim :> FL Prim) a)) where+instance Arbitrary (Sealed (WithState V1Model (FL Prim2 :> FL Prim2) a)) where arbitrary = makeWSGen ourSmallRepo
@@ -15,20 +15,16 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP, FlexibleInstances, TypeSynonymInstances #-}-+{-# LANGUAGE 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 ) @@ -36,8 +32,8 @@ 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.V1.Core ( RepoPatchV1(..) )+import qualified Darcs.Patch.V1.Prim as V1 ( Prim(..) ) import Darcs.Patch.Prim.V1.Core ( Prim(..) ) import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), unseal, mapSeal, Sealed2(..) )@@ -49,9 +45,10 @@ import Darcs.Test.Patch.Properties.Check( checkAPatch ) import Darcs.Test.Patch.Arbitrary.Generic ( MightHaveDuplicate ) -#include "impossible.h"+type Patch = RepoPatchV1 V1.Prim -type Patch = V1.RepoPatchV1 Prim+pp :: Prim wX wY -> Patch wX wY+pp = PP . V1.Prim class ArbitraryP p where arbitraryP :: Gen (Sealed (p wX))@@ -112,7 +109,7 @@ instance ArbitraryP Prim where arbitraryP = onepatchgen -instance MightHaveDuplicate (V1.RepoPatchV1 prim)+instance MightHaveDuplicate (RepoPatchV1 prim) hunkgen :: Gen (Sealed (Prim wX)) hunkgen = do@@ -165,25 +162,18 @@ 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]+norecursgen 0 = mapSeal (\p -> pp p :>: NilFL) `fmap` onepatchgen+norecursgen n = oneof [mapSeal (\p -> 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),+arbpatch 0 = mapSeal (\p -> pp p :>: NilFL) `fmap` onepatchgen+arbpatch n = frequency [(3,mapSeal (\p -> pp p :>: NilFL) `fmap` onepatchgen), (2,flatcompgen n), (0,rawMergeGen n), (0,mergegen n),- (1,mapSeal (\p -> V1.PP p :>: NilFL) `fmap` onepatchgen)+ (1,mapSeal (\p -> 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@@ -208,16 +198,6 @@ 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 @@ -225,7 +205,7 @@ flatlistgen 0 = return $ Sealed NilFL flatlistgen n = do Sealed x <- onepatchgen Sealed xs <- flatlistgen (n-1)- return (Sealed (V1.PP x :>: xs))+ return (Sealed (pp x :>: xs)) flatcompgen :: Int -> Gen (Sealed (FL Patch wX)) flatcompgen n = do
@@ -1,4 +1,3 @@-{-# OPTIONS_GHC -fno-warn-orphans #-} {-# LANGUAGE UndecidableInstances #-} module Darcs.Test.Patch.Arbitrary.RepoPatchV2 where import Darcs.Test.Patch.Arbitrary.Generic@@ -7,7 +6,7 @@ import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Merge ( Merge(..) )-import Darcs.Patch.Patchy ( Patchy, Commute(..) )+import Darcs.Patch.Commute ( Commute(..) ) import Darcs.Patch.Prim ( PrimPatch, anIdentity ) import Darcs.Patch.V2 ( RepoPatchV2 ) import Darcs.Patch.V2.RepoPatch ( isDuplicate )@@ -22,7 +21,7 @@ nontrivialRepoPatchV2s :: PrimPatch prim => (RepoPatchV2 prim :> RepoPatchV2 prim) wX wY -> Bool nontrivialRepoPatchV2s = nontrivialCommute -nontrivialCommute :: (Patchy p, MyEq p) => (p :> p) wX wY -> Bool+nontrivialCommute :: (Commute p, Eq2 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)@@ -31,7 +30,7 @@ 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 :: (Eq2 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)
@@ -15,7 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-} module Darcs.Test.Patch.Check ( PatchCheck(), doCheck, fileExists, dirExists, removeFile, removeDir, createFile, createDir, insertLine, deleteLine, isValid, doVerboseCheck,@@ -23,6 +22,9 @@ checkMove, modifyFile, FileContents(..) ) where +import Prelude ()+import Darcs.Prelude+ import System.IO.Unsafe ( unsafePerformIO ) import qualified Data.ByteString as B (ByteString) import Data.List ( isPrefixOf, inits )@@ -32,8 +34,6 @@ import Data.Map ( Map ) import qualified Data.Map as M ( mapKeys, delete, insert, empty, lookup, null ) import System.FilePath ( joinPath, splitDirectories )--#include "impossible.h" -- | File contents are represented by a map from line numbers to line contents. -- If for a certain line number, the line contents are Nothing, that means
@@ -15,9 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-deprecations #-}-{-# LANGUAGE CPP #-}- module Darcs.Test.Patch.Examples.Set1 ( knownCommutes, knownCantCommutes, knownMerges , knownMergeEquivs, knownCanons, mergePairs2@@ -25,6 +22,8 @@ , primitiveTestPatches, testPatches, testPatchesNamed , primitiveCommutePairs ) where +import Prelude ()+import Darcs.Prelude import System.IO.Unsafe ( unsafePerformIO ) import qualified Data.ByteString.Char8 as BC ( pack ) import qualified Data.ByteString as B ( empty )@@ -34,16 +33,14 @@ , readPatch, fromPrim , 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 ( RepoPatchV1 )+import qualified Darcs.Patch.V1.Prim as V1 ( Prim(..) ) import Darcs.Test.Patch.Properties.Check( checkAPatch ) import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Witnesses.Sealed ( unsafeUnseal ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP, unsafeCoercePEnd ) -#include "impossible.h"--type Patch = V1.RepoPatchV1 Prim+type Patch = V1.RepoPatchV1 V1.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@@ -86,7 +83,7 @@ (quickhunk 1 "abcde" "cde" :>: NilFL, quickhunk 1 "ab" "" :>: NilFL), (quickhunk 1 "abcde" "acde" :>: NilFL, quickhunk 2 "b" "" :>: NilFL)] -quickhunk :: (FromPrim p, PrimOf p ~ Prim) => Int -> String -> String -> p wX wY+quickhunk :: (FromPrim p, PrimOf p ~ V1.Prim) => Int -> String -> String -> p wX wY quickhunk l o n = fromPrim $ hunk "test" l (map (\c -> BC.pack [c]) o) (map (\c -> BC.pack [c]) n)
@@ -15,10 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# OPTIONS_GHC -fno-warn-deprecations -fno-warn-orphans #-}-{-# LANGUAGE CPP #-}-- module Darcs.Test.Patch.Examples.Set2Unwitnessed ( primPermutables, primPatches , commutables, commutablesFL@@ -30,11 +26,9 @@ import qualified Data.ByteString.Char8 as BC ( pack ) import Darcs.Patch.Witnesses.Sealed import Darcs.Patch ( invert, hunk )-import Darcs.Patch.Patchy ( Invert(..) )-import Darcs.Patch.Prim ( PrimPatch )-import Darcs.Patch.Prim.V1 ( Prim )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Prim ( PrimPatch, fromPrim ) 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 )@@ -45,14 +39,14 @@ import Darcs.Util.Printer.Color () -- for instance Show Doc import Darcs.Test.Patch.WSub + import qualified Darcs.Patch.Witnesses.Ordered as W ( (:>), (:\/:) ) import qualified Data.ByteString as B ( ByteString ) import Darcs.Test.Patch.V1Model ( V1Model, Content , makeRepo, makeFile) import Darcs.Test.Patch.WithState ( WithStartState(..) )-import Darcs.Patch.Prim.V1.Core ( Prim(FP), FilePatchType(Hunk) )-import Darcs.Util.Path ( FileName, fp2fn, makeName )-import Darcs.Patch.Prim ( PrimPatchBase(..), FromPrim )+import Darcs.Util.Path ( makeName )+import Darcs.Patch.Prim ( PrimPatchBase(..), FromPrim, PrimConstruct(..) ) import Darcs.Patch.Merge ( Merge ) import Darcs.Test.Patch.Arbitrary.Generic ( Tree(..)@@ -63,209 +57,210 @@ ) -- import Debug.Trace--- #include "impossible.h" +type Patch = RepoPatchV2 Prim2+ makeSimpleRepo :: String -> Content -> V1Model wX makeSimpleRepo filename content = makeRepo [(makeName filename, makeFile content)] -w_tripleExamples :: (FromPrim p, Merge p, Invert p, PrimPatchBase p, PrimOf p ~ Prim) => [Sealed2 (p W.:> p W.:> p)]+w_tripleExamples :: (FromPrim p, Merge p, Invert p, PrimPatchBase p, PrimConstruct (PrimOf p)) => [Sealed2 (p W.:> p W.:> p)] w_tripleExamples = [commuteTripleFromTree seal2 $ WithStartState (makeSimpleRepo "file" []) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "g"]))- (SeqTree (FP (fp2fn "./file") (Hunk 2 [] [BC.pack "j"]))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "s"])) NilTree)))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "e"])) NilTree))+ (SeqTree (hunk "file" 1 [] [BC.pack "g"])+ (SeqTree (hunk "file" 2 [] [BC.pack "j"])+ (SeqTree (hunk "file" 1 [] [BC.pack "s"]) NilTree)))+ (SeqTree (hunk "file" 1 [] [BC.pack "e"]) NilTree)) ,commuteTripleFromTree seal2 $ WithStartState (makeSimpleRepo "file" [BC.pack "j"]) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "s"]))+ (SeqTree (hunk "file" 1 [] [BC.pack "s"]) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 2 [BC.pack "j"] [])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 2 [BC.pack "j"] [])) NilTree)))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "j"] [])) NilTree))+ (SeqTree (hunk "file" 2 [BC.pack "j"] []) NilTree)+ (SeqTree (hunk "file" 2 [BC.pack "j"] []) NilTree)))+ (SeqTree (hunk "file" 1 [BC.pack "j"] []) NilTree)) ] -w_mergeExamples :: (FromPrim p, Merge p, Invert p, PrimPatchBase p, PrimOf p ~ Prim) => [Sealed2 (p W.:\/: p)]+w_mergeExamples :: (FromPrim p, Merge p, Invert p, PrimPatchBase p, PrimConstruct (PrimOf p)) => [Sealed2 (p W.:\/: p)] w_mergeExamples = map (unseal2 (mergePairFromCommutePair seal2)) w_commuteExamples -w_commuteExamples :: (FromPrim p, Merge p, PrimPatchBase p, PrimOf p ~ Prim) => [Sealed2 (p W.:> p)]+w_commuteExamples :: (FromPrim p, Merge p, PrimPatchBase p, PrimConstruct (PrimOf p)) => [Sealed2 (p W.:> p)] w_commuteExamples = [ commutePairFromTWFP seal2 $ WithStartState (makeSimpleRepo "file" []) (TWFP 3 (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "h"])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "b"]))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "f"]))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "v"]))- (SeqTree (FP (fp2fn "./file") (Hunk 2 [BC.pack "f"] [])) NilTree)))))),+ (SeqTree (hunk "file" 1 [] [BC.pack "h"]) NilTree)+ (SeqTree (hunk "file" 1 [] [BC.pack "b"])+ (SeqTree (hunk "file" 1 [] [BC.pack "f"])+ (SeqTree (hunk "file" 1 [] [BC.pack "v"])+ (SeqTree (hunk "file" 2 [BC.pack "f"] []) NilTree)))))), commutePairFromTWFP seal2 $ WithStartState (makeSimpleRepo "file" [BC.pack "f",BC.pack "s",BC.pack "d"]) (TWFP 3 (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 3 [BC.pack "d"] [])) NilTree)+ (SeqTree (hunk "file" 3 [BC.pack "d"] []) NilTree) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "f"] [])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "f"] []))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "s",BC.pack "d"] []))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "v"])) NilTree)))))),+ (SeqTree (hunk "file" 1 [BC.pack "f"] []) NilTree)+ (SeqTree (hunk "file" 1 [BC.pack "f"] [])+ (SeqTree (hunk "file" 1 [BC.pack "s",BC.pack "d"] [])+ (SeqTree (hunk "file" 1 [] [BC.pack "v"]) NilTree)))))), {- commutePairFromTWFP seal2 $ WithStartState (makeSimpleRepo "file" [BC.pack "f",BC.pack "u", BC.pack "s",BC.pack "d"]) (TWFP 5 (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 5 [] [BC.pack "x"]))- (SeqTree (FP (fp2fn "./file") (Hunk 4 [BC.pack "d"] [])) NilTree))+ (SeqTree (hunk "file" 5 [] [BC.pack "x"])+ (SeqTree (hunk "file" 4 [BC.pack "d"] []) NilTree)) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "f",BC.pack "u"] [])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "f"] []))- (SeqTree (FP(fp2fn "./file") (Hunk 1 [BC.pack "u",BC.pack "s",BC.pack "d"] []))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "a"]))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "a"] [])) NilTree))))))),-}+ (SeqTree (hunk "file" 1 [BC.pack "f",BC.pack "u"] []) NilTree)+ (SeqTree (hunk "file" 1 [BC.pack "f"] [])+ (SeqTree (hunk "file" 1 [BC.pack "u",BC.pack "s",BC.pack "d"] [])+ (SeqTree (hunk "file" 1 [] [BC.pack "a"])+ (SeqTree (hunk "file" 1 [BC.pack "a"] []) NilTree))))))),-} commutePairFromTree seal2 $ WithStartState (makeSimpleRepo "file" [BC.pack "n",BC.pack "t",BC.pack "h"]) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "n",BC.pack "t",BC.pack "h"] []))+ (SeqTree (hunk "file" 1 [BC.pack "n",BC.pack "t",BC.pack "h"] []) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 3 [BC.pack "h"] []))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "n"] []))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "t"] [])) NilTree)))),+ (SeqTree (hunk "file" 3 [BC.pack "h"] [])+ (SeqTree (hunk "file" 1 [BC.pack "n"] [])+ (SeqTree (hunk "file" 1 [BC.pack "t"] []) NilTree)))), commutePairFromTree seal2 $ WithStartState (makeSimpleRepo "file" []) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "n"])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "i"]))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "i"])) NilTree))),+ (SeqTree (hunk "file" 1 [] [BC.pack "n"]) NilTree)+ (SeqTree (hunk "file" 1 [] [BC.pack "i"])+ (SeqTree (hunk "file" 1 [] [BC.pack "i"]) NilTree))), commutePairFromTree seal2 $ WithStartState (makeSimpleRepo "file" []) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "c"]))+ (SeqTree (hunk "file" 1 [] [BC.pack "c"]) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "c"] [BC.pack "r"])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "h"]))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "d"])) NilTree))))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "f"])) NilTree)),+ (SeqTree (hunk "file" 1 [BC.pack "c"] [BC.pack "r"]) NilTree)+ (SeqTree (hunk "file" 1 [] [BC.pack "h"])+ (SeqTree (hunk "file" 1 [] [BC.pack "d"]) NilTree))))+ (SeqTree (hunk "file" 1 [] [BC.pack "f"]) NilTree)), commutePairFromTWFP seal2 $ WithStartState (makeSimpleRepo "file" []) (TWFP 1 (ParTree (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "t"])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "t"])) NilTree))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "f"])) NilTree))),+ (SeqTree (hunk "file" 1 [] [BC.pack "t"]) NilTree)+ (SeqTree (hunk "file" 1 [] [BC.pack "t"]) NilTree))+ (SeqTree (hunk "file" 1 [] [BC.pack "f"]) NilTree))), commutePairFromTWFP seal2 $ WithStartState (makeSimpleRepo "file" [BC.pack "f",BC.pack " r", BC.pack "c",BC.pack "v"]) (TWFP 4 (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 3 [BC.pack "c",BC.pack "v"] []))+ (SeqTree (hunk "file" 3 [BC.pack "c",BC.pack "v"] []) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 2 [BC.pack "r"] []))- (SeqTree (FP (fp2fn "fi le") (Hunk 1 [BC.pack "f"] [])) NilTree))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "f",BC.pack "r"] []))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "y"])) NilTree))))- (SeqTree (FP (fp2fn "./file") (Hunk 4 [BC.pack "v"] [])) NilTree))),+ (SeqTree (hunk "file" 2 [BC.pack "r"] [])+ (SeqTree (hunk "fi le" 1 [BC.pack "f"] []) NilTree))+ (SeqTree (hunk "file" 1 [BC.pack "f",BC.pack "r"] [])+ (SeqTree (hunk "file" 1 [] [BC.pack "y"]) NilTree))))+ (SeqTree (hunk "file" 4 [BC.pack "v"] []) NilTree))), commutePairFromTree seal2 $ WithStartState (makeSimpleRepo "file" []) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "z"])) NilTree)+ (SeqTree (hunk "file" 1 [] [BC.pack "z"]) NilTree) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "f"])) NilTree)+ (SeqTree (hunk "file" 1 [] [BC.pack "f"]) NilTree) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "r"])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "d"])) NilTree))))+ (SeqTree (hunk "file" 1 [] [BC.pack "r"]) NilTree)+ (SeqTree (hunk "file" 1 [] [BC.pack "d"]) NilTree)))) , commutePairFromTree seal2 $ WithStartState (makeSimpleRepo "file" [BC.pack "t",BC.pack "r",BC.pack "h"]) (ParTree (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "t",BC.pack "r",BC.pack "h"] []))+ (SeqTree (hunk "file" 1 [BC.pack "t",BC.pack "r",BC.pack "h"] []) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "o"])) NilTree))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "t"] []))- (SeqTree (FP (fp2fn "./file") (Hunk 2 [BC.pack "h"] [])) NilTree)))+ (SeqTree (hunk "file" 1 [] [BC.pack "o"]) NilTree))+ (SeqTree (hunk "file" 1 [BC.pack "t"] [])+ (SeqTree (hunk "file" 2 [BC.pack "h"] []) NilTree))) , commutePairFromTWFP seal2 $ WithStartState (makeSimpleRepo "file" []) $ TWFP 2 (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "h"])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "y"]))- (SeqTree (FP (fp2fn "./file") (Hunk 2 [] [BC.pack "m"]))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "v"])) NilTree))))+ (SeqTree (hunk "file" 1 [] [BC.pack "h"]) NilTree)+ (SeqTree (hunk "file" 1 [] [BC.pack "y"])+ (SeqTree (hunk "file" 2 [] [BC.pack "m"])+ (SeqTree (hunk "file" 1 [] [BC.pack "v"]) NilTree)))) , commutePairFromTree seal2 $ WithStartState (makeSimpleRepo "file" []) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "p"]))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "p"] []))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "c"])) NilTree)))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "z"])) NilTree))+ (SeqTree (hunk "file" 1 [] [BC.pack "p"])+ (SeqTree (hunk "file" 1 [BC.pack "p"] [])+ (SeqTree (hunk "file" 1 [] [BC.pack "c"]) NilTree)))+ (SeqTree (hunk "file" 1 [] [BC.pack "z"]) NilTree)) , commutePairFromTree seal2 $ WithStartState (makeSimpleRepo "file" []) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "j" ]))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "j"] [])) NilTree))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "v"])) NilTree))+ (SeqTree (hunk "file" 1 [] [BC.pack "j" ])+ (SeqTree (hunk "file" 1 [BC.pack "j"] []) NilTree))+ (SeqTree (hunk "file" 1 [] [BC.pack "v"]) NilTree)) , commutePairFromTree seal2 $ WithStartState (makeSimpleRepo "file" []) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "v"])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "j" ]))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [BC.pack "j"] [])) NilTree)))+ (SeqTree (hunk "file" 1 [] [BC.pack "v"]) NilTree)+ (SeqTree (hunk "file" 1 [] [BC.pack "j" ])+ (SeqTree (hunk "file" 1 [BC.pack "j"] []) NilTree))) , commutePairFromTree seal2 $ WithStartState (makeSimpleRepo "file" [BC.pack "x",BC.pack "c"]) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "h"]))+ (SeqTree (hunk "file" 1 [] [BC.pack "h"]) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 3 [BC.pack "c"] [])) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 2 [BC.pack "x"] []))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "j"])) NilTree))))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] [BC.pack "l"])) NilTree))+ (SeqTree (hunk "file" 3 [BC.pack "c"] []) NilTree)+ (SeqTree (hunk "file" 2 [BC.pack "x"] [])+ (SeqTree (hunk "file" 1 [] [BC.pack "j"]) NilTree))))+ (SeqTree (hunk "file" 1 [] [BC.pack "l"]) NilTree)) , commutePairFromTree seal2 $ WithStartState (makeSimpleRepo "file" []) (ParTree- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] (packStringLetters "s"))) NilTree)- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] (packStringLetters "k")))- (SeqTree (FP (fp2fn "./file") (Hunk 1 (packStringLetters "k") []))- (SeqTree (FP (fp2fn "./file") (Hunk 1 [] (packStringLetters "m")))- (SeqTree (FP (fp2fn "./file") (Hunk 1 (packStringLetters "m") [])) NilTree)))))+ (SeqTree (hunk "file" 1 [] (packStringLetters "s")) NilTree)+ (SeqTree (hunk "file" 1 [] (packStringLetters "k"))+ (SeqTree (hunk "file" 1 (packStringLetters "k") [])+ (SeqTree (hunk "file" 1 [] (packStringLetters "m"))+ (SeqTree (hunk "file" 1 (packStringLetters "m") []) NilTree))))) ] packStringLetters :: String -> [B.ByteString] packStringLetters = map (BC.pack . (:[])) -w_repov2PatchLoopExamples :: [Sealed (WithStartState V1Model (Tree Prim))]+w_repov2PatchLoopExamples :: [Sealed (WithStartState V1Model (Tree Prim2))] w_repov2PatchLoopExamples =- [Sealed (WithStartState (makeSimpleRepo fx_name [])+ [Sealed (WithStartState (makeSimpleRepo fx []) $ canonizeTree (ParTree- (SeqTree (FP fx (Hunk 1 [] (packStringLetters "pkotufogbvdabnmbzajvolwviqebieonxvcvuvigkfgybmqhzuaaurjspd")))+ (SeqTree (hunk fx 1 [] (packStringLetters "pkotufogbvdabnmbzajvolwviqebieonxvcvuvigkfgybmqhzuaaurjspd")) (ParTree- (SeqTree (FP fx (Hunk 47 (packStringLetters "qhzu") (packStringLetters "zafybdcokyjskcgnvhkbzpysaafnjjhcstgrczplxsfwagmh")))+ (SeqTree (hunk fx 47 (packStringLetters "qhzu") (packStringLetters "zafybdcokyjskcgnvhkbzpysaafnjjhcstgrczplxsfwagmh")) (ParTree (ParTree NilTree (ParTree (ParTree (ParTree- (SeqTree (FP fx (Hunk 15 (packStringLetters "mbzajvolwviqebieonxvcvuvigkfgyb") (packStringLetters "vujnxnhvybvpouyciaabszfmgssezlwwjgnethvrpnfrkubphzvdgymjjoacppqps")))+ (SeqTree (hunk fx 15 (packStringLetters "mbzajvolwviqebieonxvcvuvigkfgyb") (packStringLetters "vujnxnhvybvpouyciaabszfmgssezlwwjgnethvrpnfrkubphzvdgymjjoacppqps")) (ParTree NilTree (ParTree- (SeqTree (FP fx (Hunk 40 (packStringLetters "ssezlwwjgnethvrpnfrkubphzvdgymjjoacppqpsmzafybdcokyjskcgnvhkbz") (packStringLetters "wnesidpccwoiqiichxaaejdsyrhrusqljlcoro")))+ (SeqTree (hunk fx 40 (packStringLetters "ssezlwwjgnethvrpnfrkubphzvdgymjjoacppqpsmzafybdcokyjskcgnvhkbz") (packStringLetters "wnesidpccwoiqiichxaaejdsyrhrusqljlcoro")) (ParTree (ParTree- (SeqTree (FP fx (Hunk 12 (packStringLetters "abnvujnxnhvybvpouyciaabszfmgwnesidpccwoiqii") (packStringLetters "czfdhqkipdstfjycqaxwnbxrihrufdeyneqiiiafwzlmg"))) NilTree)+ (SeqTree (hunk fx 12 (packStringLetters "abnvujnxnhvybvpouyciaabszfmgwnesidpccwoiqii") (packStringLetters "czfdhqkipdstfjycqaxwnbxrihrufdeyneqiiiafwzlmg")) NilTree) NilTree) NilTree))- (SeqTree (FP fx (Hunk 25 [] (packStringLetters "dihgmsotezucqdgxczvcivijootyvhlwymbiueufnvpwpeukmskqllalfe"))) NilTree))))- (SeqTree (FP fx (Hunk 56 (packStringLetters "yjskcgnvhkbzpysaafnjjhcstgrczplxsfwagmhaaurjsp") (packStringLetters "xldhrutyhcyaqeezwujiguawfyawjjqlirxshjddvq"))) NilTree))- (SeqTree (FP fx (Hunk 20 [] (packStringLetters "ooygwiyogqrqnytixqtmvdxx")))- (SeqTree (FP fx (Hunk 26 (packStringLetters "yogqrqnytixqtmvdxxvolwviqebieonxvcvuvigkfgybmzafybdcokyjskcgnvhkbz") (packStringLetters "akhsmlbkdxnvfoikmiatfbpzdrsyykkpoxvvddeaspzxe")))- (SeqTree (FP fx (Hunk 39 [] (packStringLetters "ji")))+ (SeqTree (hunk fx 25 [] (packStringLetters "dihgmsotezucqdgxczvcivijootyvhlwymbiueufnvpwpeukmskqllalfe")) NilTree))))+ (SeqTree (hunk fx 56 (packStringLetters "yjskcgnvhkbzpysaafnjjhcstgrczplxsfwagmhaaurjsp") (packStringLetters "xldhrutyhcyaqeezwujiguawfyawjjqlirxshjddvq")) NilTree))+ (SeqTree (hunk fx 20 [] (packStringLetters "ooygwiyogqrqnytixqtmvdxx"))+ (SeqTree (hunk fx 26 (packStringLetters "yogqrqnytixqtmvdxxvolwviqebieonxvcvuvigkfgybmzafybdcokyjskcgnvhkbz") (packStringLetters "akhsmlbkdxnvfoikmiatfbpzdrsyykkpoxvvddeaspzxe"))+ (SeqTree (hunk fx 39 [] (packStringLetters "ji")) (ParTree NilTree (ParTree@@ -273,14 +268,14 @@ (ParTree (ParTree NilTree- (SeqTree (FP fx (Hunk 26 (packStringLetters "akhsmlbkdxnvfjioikmiatfbpzdrsyykkpoxvvddeaspzxepysaafnjjhcstgrczplxs") (packStringLetters "onjbhddskcj")))- (SeqTree (FP fx (Hunk 39 [] (packStringLetters "fyscunxxxjjtyqpfxeznhtwvlphmp"))) NilTree)))+ (SeqTree (hunk fx 26 (packStringLetters "akhsmlbkdxnvfjioikmiatfbpzdrsyykkpoxvvddeaspzxepysaafnjjhcstgrczplxs") (packStringLetters "onjbhddskcj"))+ (SeqTree (hunk fx 39 [] (packStringLetters "fyscunxxxjjtyqpfxeznhtwvlphmp")) NilTree))) (ParTree NilTree- (SeqTree (FP fx (Hunk 44 [] (packStringLetters "xcchzwmzoezxkmkhcmesplnjpqriypshgiqklgdnbmmkldnydiy")))+ (SeqTree (hunk fx 44 [] (packStringLetters "xcchzwmzoezxkmkhcmesplnjpqriypshgiqklgdnbmmkldnydiy")) (ParTree NilTree- (SeqTree (FP fx (Hunk 64 (packStringLetters "plnjpqriypshgiqklgdnbmmkldnydiymiatfbpzdrsyykkpoxvvddeaspzxepysaafn") (packStringLetters "anjlzfdqbjqbcplvqvkhwjtkigp"))) NilTree)))))))))))+ (SeqTree (hunk fx 64 (packStringLetters "plnjpqriypshgiqklgdnbmmkldnydiymiatfbpzdrsyykkpoxvvddeaspzxepysaafn") (packStringLetters "anjlzfdqbjqbcplvqvkhwjtkigp")) NilTree))))))))))) (ParTree NilTree NilTree)))@@ -288,46 +283,42 @@ NilTree)) (ParTree NilTree- (SeqTree (FP fx (Hunk 1 [] (packStringLetters "ti")))- (SeqTree (FP fx (Hunk 1 (packStringLetters "t") (packStringLetters "ybcop")))- (SeqTree (FP fx (Hunk 2 [] (packStringLetters "dvlhgwqlpaeweerqrhnjtfolczbqbzoccnvdsyqiefqitrqneralf")))- (SeqTree (FP fx (Hunk 15 [] (packStringLetters "yairbjphwtnaerccdlfewujvjvmjakbc")))- (SeqTree (FP fx (Hunk 51 [] (packStringLetters "xayvfuwaiiogginufnhsrmktpmlbvxiakjwllddkiyofyfw")))+ (SeqTree (hunk fx 1 [] (packStringLetters "ti"))+ (SeqTree (hunk fx 1 (packStringLetters "t") (packStringLetters "ybcop"))+ (SeqTree (hunk fx 2 [] (packStringLetters "dvlhgwqlpaeweerqrhnjtfolczbqbzoccnvdsyqiefqitrqneralf"))+ (SeqTree (hunk fx 15 [] (packStringLetters "yairbjphwtnaerccdlfewujvjvmjakbc"))+ (SeqTree (hunk fx 51 [] (packStringLetters "xayvfuwaiiogginufnhsrmktpmlbvxiakjwllddkiyofyfw")) (ParTree NilTree NilTree)))))))))] where- fx_name :: String- fx_name = "F"-- fx :: FileName- fx = fp2fn "./F"-+ fx :: String+ fx = "F" -mergeExamples :: [Sealed2 (RepoPatchV2 Prim :\/: RepoPatchV2 Prim)]+mergeExamples :: [Sealed2 (Patch :\/: Patch)] mergeExamples = map (mapSeal2 fromW) w_mergeExamples -repov2PatchLoopExamples :: [Sealed (WithStartState V1Model (Tree Prim))]+repov2PatchLoopExamples :: [Sealed (WithStartState V1Model (Tree Prim2))] repov2PatchLoopExamples = w_repov2PatchLoopExamples -commuteExamples :: [Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim)]+commuteExamples :: [Sealed2 (Patch :> Patch)] commuteExamples = map (mapSeal2 fromW) w_commuteExamples -tripleExamples :: [Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim :> RepoPatchV2 Prim)]+tripleExamples :: [Sealed2 (Patch :> Patch :> Patch)] tripleExamples = map (mapSeal2 fromW) w_tripleExamples -notDuplicatestriple :: (RepoPatchV2 Prim :> RepoPatchV2 Prim :> RepoPatchV2 Prim) wX wY -> Bool+notDuplicatestriple :: (Patch :> Patch :> Patch) wX wY -> Bool notDuplicatestriple = W.notDuplicatestriple . toW quickhunk :: PrimPatch prim => Int -> String -> String -> prim wX wY quickhunk l o n = hunk "test" l (map (\c -> BC.pack [c]) o) (map (\c -> BC.pack [c]) n) -primPermutables :: [(Prim :> Prim :> Prim) wX wY]+primPermutables :: [(Prim2 :> Prim2 :> Prim2) wX wY] primPermutables = [quickhunk 0 "e" "bo" :> quickhunk 3 "" "x" :> quickhunk 2 "f" "qljo"] -mergeables :: [(Prim :\/: Prim) wX wY]+mergeables :: [(Prim2 :\/: Prim2) wX wY] mergeables = [quickhunk 1 "a" "b" :\/: quickhunk 1 "a" "c", quickhunk 1 "a" "b" :\/: quickhunk 3 "z" "c", quickhunk 0 "" "a" :\/: quickhunk 1 "" "b",@@ -336,7 +327,7 @@ quickhunk 0 "" "a" :\/: quickhunk 1 "b" "" ] -mergeablesFL :: [(FL Prim :\/: FL Prim) wX wY]+mergeablesFL :: [(FL Prim2 :\/: FL Prim2) wX wY] mergeablesFL = map (\ (x:\/:y) -> (x :>: NilFL) :\/: (y :>: NilFL)) mergeables ++ [] -- [(quickhunk 1 "a" "b" :>: quickhunk 3 "z" "c" :>: NilFL) -- :\/: (quickhunk 1 "a" "z" :>: NilFL),@@ -346,50 +337,50 @@ mergeable2commutable :: Invert p => (p :\/: p) wX wY -> (p :> p) wX wY mergeable2commutable (x :\/: y) = unsafeCoerceP (invert x) :> y -commutablesFL :: [(FL Prim :> FL Prim) wX wY]+commutablesFL :: [(FL Prim2 :> FL Prim2) wX wY] commutablesFL = map mergeable2commutable mergeablesFL-commutables :: [(Prim :> Prim) wX wY]+commutables :: [(Prim2 :> Prim2) wX wY] commutables = map mergeable2commutable mergeables -primPatches :: [Prim wX wY]+primPatches :: [Prim2 wX wY] primPatches = concatMap mergeable2patches mergeables where mergeable2patches (x:\/:y) = [x,y] -repov2Patches :: [RepoPatchV2 Prim wX wY]+repov2Patches :: [Patch wX wY] repov2Patches = concatMap commutable2patches repov2Commutables where commutable2patches (x:>y) = [x,y] -repov2Triples :: [(RepoPatchV2 Prim :> RepoPatchV2 Prim :> RepoPatchV2 Prim) wX wY]+repov2Triples :: [(Patch :> Patch :> Patch) wX wY] repov2Triples = [ob' :> oa2 :> a2'', oa' :> oa2 :> a2''] ++ map unsafeUnseal2 tripleExamples ++ map unsafeUnseal2 (concatMap getTriples repov2FLs)- where oa = prim2repopatchV2 $ quickhunk 1 "o" "aa"+ where oa = fromPrim $ quickhunk 1 "o" "aa" oa2 = oa- a2 = prim2repopatchV2 $ quickhunk 2 "a34" "2xx"- ob = prim2repopatchV2 $ quickhunk 1 "o" "bb"+ a2 = fromPrim $ quickhunk 2 "a34" "2xx"+ ob = fromPrim $ quickhunk 1 "o" "bb" ob' :/\: oa' = merge (oa :\/: ob) a2' :/\: _ = merge (ob' :\/: a2) a2'' :/\: _ = merge (oa2 :\/: a2') -repov2NonduplicateTriples :: [(RepoPatchV2 Prim :> RepoPatchV2 Prim :> RepoPatchV2 Prim) wX wY]+repov2NonduplicateTriples :: [(Patch :> Patch :> Patch) wX wY] repov2NonduplicateTriples = filter (notDuplicatestriple) repov2Triples -repov2FLs :: [FL (RepoPatchV2 Prim) wX wY]+repov2FLs :: [FL (Patch) wX wY] repov2FLs = [oa :>: invert oa :>: oa :>: invert oa :>: ps +>+ oa :>: invert oa :>: NilFL]- where oa = prim2repopatchV2 $ quickhunk 1 "o" "a"+ where oa = fromPrim $ quickhunk 1 "o" "a" ps :/\: _ = merge (oa :>: invert oa :>: NilFL :\/: oa :>: invert oa :>: NilFL) -repov2Commutables :: [(RepoPatchV2 Prim :> RepoPatchV2 Prim) wX wY]+repov2Commutables :: [(Patch :> Patch) 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"+ where oa = fromPrim $ quickhunk 1 "o" "a"+ ob = fromPrim $ quickhunk 1 "o" "b" _ :/\: ob' = mergeFL (ob :\/: oa :>: invert oa :>: NilFL) -repov2Mergeables :: [(RepoPatchV2 Prim :\/: RepoPatchV2 Prim) wX wY]-repov2Mergeables = map (\ (x :\/: y) -> prim2repopatchV2 x :\/: prim2repopatchV2 y) mergeables+repov2Mergeables :: [(Patch :\/: Patch) wX wY]+repov2Mergeables = map (\ (x :\/: y) -> fromPrim x :\/: fromPrim y) mergeables ++ repov2IglooMergeables ++ repov2QuickcheckMergeables ++ map unsafeUnseal2 mergeExamples@@ -410,16 +401,16 @@ (ob'' :\/: og''), (ob'' :\/: oc''), (oc' :\/: od'')]- 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"+ where oa = fromPrim $ quickhunk 1 "o" "aa"+ a2 = fromPrim $ quickhunk 2 "a34" "2xx"+ og = fromPrim $ quickhunk 3 "4" "g"+ ob = fromPrim $ quickhunk 1 "o" "bb"+ b2 = fromPrim $ quickhunk 2 "b" "2"+ oc = fromPrim $ quickhunk 1 "o" "cc"+ od = fromPrim $ quickhunk 7 "x" "d"+ oe = fromPrim $ quickhunk 7 "x" "e"+ pf = fromPrim $ quickhunk 7 "x" "f"+ od'' = fromPrim $ quickhunk 8 "x" "d" ob' :>: b2' :>: NilFL :/\: _ = mergeFL (oa :\/: ob :>: b2 :>: NilFL) a2' :/\: _ = merge (ob' :\/: a2) ob'' :/\: _ = merge (a2 :\/: ob')@@ -431,12 +422,12 @@ oc''' :/\: _ = merge (ob' :\/: oc') oe' :/\: _ = merge (od :\/: oe) of' :/\: _ = merge (od :\/: pf)- pair2m :: Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim)- -> Maybe ((RepoPatchV2 Prim :\/: RepoPatchV2 Prim) wX wY)+ pair2m :: Sealed2 (Patch :> Patch)+ -> Maybe ((Patch :\/: Patch) wX wY) pair2m (Sealed2 (xx :> y)) = do y' :> _ <- commute (xx :> y) return $ unsafeCoerceP (xx :\/: y') -repov2IglooMergeables :: [(RepoPatchV2 Prim :\/: RepoPatchV2 Prim) wX wY]+repov2IglooMergeables :: [(Patch :\/: Patch) wX wY] repov2IglooMergeables = [(a :\/: b), (b :\/: c), (a :\/: c),@@ -447,17 +438,17 @@ (z' :\/: y'), (x' :\/: z'), (a :\/: a)]- 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"+ where a = fromPrim $ quickhunk 1 "1" "A"+ b = fromPrim $ quickhunk 2 "2" "B"+ c = fromPrim $ quickhunk 3 "3" "C"+ x = fromPrim $ quickhunk 1 "1BC" "xbc"+ y = fromPrim $ quickhunk 1 "A2C" "ayc"+ z = fromPrim $ quickhunk 1 "AB3" "abz" x' :/\: _ = merge (a :\/: x) y' :/\: _ = merge (b :\/: y) z' :/\: _ = merge (c :\/: z) -repov2QuickcheckMergeables :: [(RepoPatchV2 Prim :\/: RepoPatchV2 Prim) wX wY]+repov2QuickcheckMergeables :: [(Patch :\/: Patch) wX wY] repov2QuickcheckMergeables = [-- invert k1 :\/: n1 --, invert k2 :\/: n2 hb :\/: k@@ -467,25 +458,25 @@ , k' :\/: k' , k3 :\/: k3 ] ++ catMaybes (map pair2m pairs)- 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"+ where hb = fromPrim $ quickhunk 0 "" "hb"+ k = fromPrim $ quickhunk 0 "" "k"+ n = fromPrim $ quickhunk 0 "" "n"+ b = fromPrim $ quickhunk 1 "b" ""+ d = fromPrim $ 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 (RepoPatchV2 Prim :> RepoPatchV2 Prim)- -> Maybe ((RepoPatchV2 Prim :\/: RepoPatchV2 Prim) wX wY)+ pair2m :: Sealed2 (Patch :> Patch)+ -> Maybe ((Patch :\/: Patch) wX wY) pair2m (Sealed2 (xx :> y)) = do y' :> _ <- commute (xx :> y) return $ unsafeCoerceP (xx :\/: y') - i = prim2repopatchV2 $ quickhunk 0 "" "i"- x = prim2repopatchV2 $ quickhunk 0 "" "x"- xi = prim2repopatchV2 $ quickhunk 0 "xi" ""+ i = fromPrim $ quickhunk 0 "" "i"+ x = fromPrim $ quickhunk 0 "" "x"+ xi = fromPrim $ quickhunk 0 "xi" "" d3 :/\: _ = merge (xi :\/: d) _ :/\: k3 = mergeFL (k :\/: i :>: x :>: xi :>: d3 :>: NilFL)
@@ -1,6 +1,4 @@-{-# OPTIONS_GHC -fno-warn-name-shadowing -fno-warn-unused-binds -fno-warn-unused-imports -fno-warn-orphans #-}-{-# LANGUAGE CPP, OverloadedStrings, MultiParamTypeClasses, StandaloneDeriving #-}-+{-# LANGUAGE OverloadedStrings, MultiParamTypeClasses, StandaloneDeriving #-} -- | Repository model module Darcs.Test.Patch.FileUUIDModel@@ -10,8 +8,8 @@ , emptyFile , emptyDir , nullRepo- , isEmpty- , root, repoObjects+ , root, rootId+ , repoObjects, repoIds , aFilename, aDirname , aLine, aContent , aFile, aDir@@ -20,41 +18,34 @@ ) where +import Prelude ()+import Darcs.Prelude+ 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.Apply( applyToState )+import Darcs.Patch.Prim.FileUUID.Core( UUID(..), 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.Path ( Name, unsafeMakeName ) 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 as B 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"-+-- import Text.Show.Pretty ( ppShow ) ---------------------------------------------------------------------- -- * Model definition -newtype FileUUIDModel wX = FileUUIDModel { repoMap :: ObjectMap Fail }+newtype FileUUIDModel wX = FileUUIDModel { _repoMap :: ObjectMap Fail } ---------------------------------------- -- Instances@@ -65,8 +56,8 @@ deriving instance Eq (Object Fail) -instance Show (FileUUIDModel x) where- show = showModel+instance Show (FileUUIDModel wX) where+ show repo = "FileUUIDModel " ++ show (repoObjects repo) instance Show1 FileUUIDModel where showDict1 = ShowDictClass@@ -75,16 +66,18 @@ -- * 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+objectMap m = ObjectMap { getObject = get, putObject = put, listObjects = list }+ where list = return $ M.keys m+ put k o = return $ objectMap (M.insert k o m)+ get k = return $ M.lookup k m +{- emptyRepo :: FileUUIDModel wX-emptyRepo = FileUUIDModel (objectMap M.empty)+emptyRepo = FileUUIDModel (objectMap $ M.singleton rootId emptyDir)+-} emptyFile :: (Monad m) => Object m-emptyFile = Blob (return BS.empty) NoHash+emptyFile = Blob (return B.empty) NoHash emptyDir :: Object m emptyDir = Directory M.empty@@ -93,22 +86,32 @@ -- * Queries nullRepo :: FileUUIDModel wX -> Bool-nullRepo = null . repoObjects+nullRepo repo = repoIds repo == [rootId] +rootId :: UUID+rootId = UUID "ROOT"++-- | The root directory of a repository.+root :: FileUUIDModel wX -> (UUID, Object Fail)+root (FileUUIDModel repo) = (rootId, fromJust $ unFail $ getObject repo rootId)++repoObjects :: FileUUIDModel wX -> [(UUID, Object Fail)]+repoObjects (FileUUIDModel repo) =+ [(uuid, obj uuid) | uuid <- unFail $ listObjects repo]+ where+ obj uuid = fromJust $ unFail $ getObject repo uuid++repoIds :: FileUUIDModel wX -> [UUID]+repoIds = map fst . 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")+isEmpty (Blob f _) = B.null $ unFail f -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+nonEmptyRepoObjects :: FileUUIDModel wX -> [(UUID, Object Fail)]+nonEmptyRepoObjects = filter (not . isEmpty . snd) . repoObjects ---------------------------------------------------------------------- -- * Comparing repositories@@ -125,29 +128,28 @@ -- | 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+aFilename :: Gen Name+aFilename = do+ len <- choose (1,3)+ name <- vectorOf len alpha+ return $ unsafeMakeName . BC.pack $ name ++ ".txt" -aDirname :: Gen BS.ByteString-aDirname = do len <- choose (1,maxLength)- BC.pack <$> vectorOf len alpha- where- maxLength = 3+aDirname :: Gen Name+aDirname = do+ len <- choose (1,3)+ name <- vectorOf len alpha+ return $ unsafeMakeName . BC.pack $ name -aWord :: Gen BS.ByteString+aWord :: Gen B.ByteString aWord = do c <- alpha return $ BC.pack[c] -aLine :: Gen BS.ByteString+aLine :: Gen B.ByteString aLine = do wordsNo <- choose (1,2) ws <- vectorOf wordsNo aWord return $ BC.unwords ws -aContent :: Gen BS.ByteString+aContent :: Gen B.ByteString aContent = bSized 0 0.5 80 $ \k -> do n <- choose (0,k) BC.intercalate "\n" <$> vectorOf n aLine@@ -161,7 +163,6 @@ 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@@ -175,28 +176,29 @@ 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+ remaining <- subdirs (tail tomake) (drop dirsplit dirs) (drop filesplit files)+ return $ dir ++ remaining anUUID :: Gen UUID-anUUID = UUID . BC.pack <$> vectorOf 32 (oneof $ map return "0123456789")+anUUID = UUID . BC.pack <$> vectorOf 4 (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+aRepo :: Int -- ^ Maximum number of files+ -> Int -- ^ Maximum number of directories+ -> Gen (FileUUIDModel wX)+aRepo maxFiles maxDirs = do+ ids <- uniques (maxFiles+maxDirs) anUUID+ 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)+ let (dirids, ids') = splitAt dirsNo ids+ fileids = take filesNo ids'+ objectmap <- aDir (rootId : dirids) fileids+ return $ FileUUIDModel $ objectMap $ M.fromList objectmap -- | Generate small repositories. -- Small repositories help generating (potentially) conflicting patches.@@ -206,9 +208,8 @@ 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+ showModel = show -- ppShow+ eqModel r1 r2 = nonEmptyRepoObjects r1 == nonEmptyRepoObjects r2 instance Arbitrary (Sealed FileUUIDModel) where arbitrary = seal <$> aSmallRepo
@@ -20,30 +20,36 @@ module Darcs.Test.Patch.Info ( testSuite ) where import Prelude hiding ( pi )-import Data.ByteString ( ByteString )-import qualified Data.ByteString as B ( split, pack )+import qualified Data.ByteString as B ( ByteString, pack ) import qualified Data.ByteString.Char8 as BC ( unpack )-import Data.List ( sort )+import Data.List ( sort , isPrefixOf ) import Data.Maybe ( isNothing ) import Data.Text as T ( find, any ) import Data.Text.Encoding ( decodeUtf8With ) import Data.Text.Encoding.Error ( lenientDecode )-import System.IO.Unsafe ( unsafePerformIO ) import Test.QuickCheck ( Arbitrary(arbitrary), oneof, listOf, choose, shrink- , Gen )+ , Gen, suchThat, scale ) import Test.Framework.Providers.QuickCheck2 ( testProperty ) import Test.Framework (Test, testGroup)-import Data.List ( isPrefixOf )+-- import Text.Show.Pretty ( ppShow ) -import Darcs.Patch.Info ( PatchInfo(..), patchinfo,- piLog, piAuthor, piName )-import Darcs.Util.ByteString ( decodeLocale, packStringToUTF8, unpackPSFromUTF8 )+import Darcs.Patch.Info+ ( PatchInfo(..), rawPatchInfo, showPatchInfo, readPatchInfo+ , piLog, piAuthor, piName, validDate, validLog, validAuthor+ , validDatePS, validLogPS, validAuthorPS+ )+import Darcs.Patch.ReadMonads ( parseStrictly )+import Darcs.Patch.Show ( ShowPatchFor(..) )+import Darcs.Util.ByteString+ ( decodeLocale, packStringToUTF8, unpackPSFromUTF8, linesPS )+import Darcs.Util.Printer ( renderPS ) testSuite :: Test testSuite = testGroup "Darcs.Patch.Info" [ metadataDecodingTest , metadataEncodingTest , packUnpackTest+ , parseUnparseTest ] -- | A newtype wrapping String so we can make our own random generator for it.@@ -58,6 +64,9 @@ -- only UTF-8-encoded PatchInfo's. newtype UTF8PatchInfo = UTF8PatchInfo PatchInfo deriving (Eq, Ord) +-- Note that this instance only creates valid unicode strings. It does not+-- generate lone surrogates, for instance, as these would fail the+-- packUnpackTest below. instance Arbitrary UnicodeString where -- 0x10ffff is the highest Unicode code point ; 0xd800 - 0xdfff are -- surrogates. '\xfffd' is excluded because it is used as a marker@@ -67,80 +76,82 @@ ,choose ('\xfffe', '\x10ffff')]) instance Show UTF8PatchInfo where- showsPrec _ = withUTF8PatchInfo rawPatchInfoShow+ show = withUTF8PatchInfo rawPatchInfoShow instance Show UTF8OrNotPatchInfo where- showsPrec _ = withUTF8OrNotPatchInfo rawPatchInfoShow+ show = withUTF8OrNotPatchInfo rawPatchInfoShow -- | Shows a PatchInfo, outputting every byte and clearly marking what is what-rawPatchInfoShow :: PatchInfo -> String -> String-rawPatchInfoShow pi = ("PatchInfo: \n"++)- . ("date: "++) . shows (_piDate pi) . ('\n':)- . ("author: "++) . shows (_piAuthor pi) . ('\n':)- . ("name: "++) . shows (_piName pi) . ('\n':)- . ("log: "++) . shows (_piLog pi) . ('\n':)+rawPatchInfoShow :: PatchInfo -> String+rawPatchInfoShow = {- ppShow -} show instance Arbitrary UTF8PatchInfo where- arbitrary = UTF8PatchInfo `fmap` arbitraryUTF8Patch+ arbitrary = UTF8PatchInfo `fmap` arbitraryUTF8PatchInfo shrink upi = flip withUTF8PatchInfo upi $ \pi -> do sn <- shrink (piName pi) sa <- shrink (piAuthor pi) sl <- shrink (filter (not . isPrefixOf "Ignore-this:") (piLog pi))- return (UTF8PatchInfo- (unsafePerformIO $ patchinfo sn- (BC.unpack (_piDate pi)) sa sl))+ i <- shrink (isInverted pi)+ return (UTF8PatchInfo (rawPatchInfo sn (BC.unpack (_piDate pi)) sa sl i)) instance Arbitrary UTF8OrNotPatchInfo where- arbitrary = UTF8OrNotPatchInfo `fmap` oneof ([arbitraryUTF8Patch,- arbitraryUnencodedPatch])+ arbitrary = UTF8OrNotPatchInfo `fmap` oneof ([arbitraryUTF8PatchInfo,+ arbitraryUnencodedPatchInfo]) --- | Generate arbitrary patch metadata that uses the metadata creation function--- 'patchinfo' from Darcs.Patch.Info.-arbitraryUTF8Patch :: Gen PatchInfo-arbitraryUTF8Patch =- do n <- asString `fmap` arbitrary- d <- arbitrary- a <- asString `fmap` arbitrary- l <- (lines . asString) `fmap` arbitrary- return $ unsafePerformIO $ patchinfo n d a l+-- | Generate arbitrary patch metadata.+-- Note : We must NOT use 'patchinfo' from Darcs.Patch.Info+-- with unsafePerformIO here because this breaks the parse/unparse test+-- (the added junk will be different on each call).+arbitraryUTF8PatchInfo :: Gen PatchInfo+arbitraryUTF8PatchInfo = do+ d <- arbitrary `suchThat` validDate+ n <- (asString `fmap` arbitrary) `suchThat` validLog+ a <- (asString `fmap` arbitrary) `suchThat` validAuthor+ l <- lines `fmap` scale (* 2) arbitrary+ i <- return False+ return $ rawPatchInfo d n a l i -- | Generate arbitrary patch metadata that has totally arbitrary byte strings -- as its name, date, author and log.-arbitraryUnencodedPatch :: Gen PatchInfo-arbitraryUnencodedPatch = do- n <- arbitraryByteString- d <- arbitraryByteString- a <- arbitraryByteString- -- split 10 is the ByteString equivalent of 'lines'- l <- B.split 10 `fmap` arbitraryByteString- i <- arbitrary+arbitraryUnencodedPatchInfo :: Gen PatchInfo+arbitraryUnencodedPatchInfo = do+ d <- arbitraryByteString `suchThat` validDatePS+ n <- arbitraryByteString `suchThat` validLogPS+ a <- arbitraryByteString `suchThat` validAuthorPS+ l <- linesPS `fmap` scale (* 2) arbitraryByteString+ i <- return False return (PatchInfo d n a l i) -arbitraryByteString :: Gen ByteString-arbitraryByteString = (B.pack . map fromIntegral)- `fmap` listOf (choose (0, 255) :: Gen Int)+arbitraryByteString :: Gen B.ByteString+arbitraryByteString = B.pack <$> listOf arbitrary -- | Test that anything produced by the 'patchinfo' function is valid UTF-8 metadataEncodingTest :: Test-metadataEncodingTest = testProperty "Testing patch metadata encoding" $- withUTF8PatchInfo $- \patchInfo -> encodingOK (_piAuthor patchInfo)- && encodingOK (_piName patchInfo)- && all encodingOK (_piLog patchInfo)- where encodingOK = isNothing . T.find (=='\xfffd') . decodeUtf8With lenientDecode+metadataEncodingTest =+ testProperty "Testing patch metadata encoding" propMetadataEncoding +propMetadataEncoding :: UTF8PatchInfo -> Bool+propMetadataEncoding (UTF8PatchInfo patchInfo) =+ encodingOK (_piAuthor patchInfo)+ && encodingOK (_piName patchInfo)+ && all encodingOK (_piLog patchInfo)+ where+ encodingOK = isNothing . T.find (=='\xfffd') . decodeUtf8With lenientDecode+ -- | Test that metadata in patches are decoded as UTF-8 or locale depending on -- whether they're valid UTF-8. metadataDecodingTest :: Test-metadataDecodingTest = testProperty "Testing patch metadata decoding" $- withUTF8OrNotPatchInfo $- \patchInfo -> utf8OrLocale (_piAuthor patchInfo) == piAuthor patchInfo- && utf8OrLocale (_piName patchInfo) == piName patchInfo- && map utf8OrLocale (_piLog patchInfo) `superset` piLog patchInfo- where utf8OrLocale bs = if isValidUTF8 bs- then unpackPSFromUTF8 bs- else decodeLocale bs+metadataDecodingTest = testProperty "Testing patch metadata decoding" propMetadataDecoding -isValidUTF8 :: ByteString -> Bool+propMetadataDecoding :: UTF8OrNotPatchInfo -> Bool+propMetadataDecoding (UTF8OrNotPatchInfo patchInfo) =+ utf8OrLocale (_piAuthor patchInfo) == piAuthor patchInfo+ && utf8OrLocale (_piName patchInfo) == piName patchInfo+ && map utf8OrLocale (_piLog patchInfo) `superset` piLog patchInfo+ where+ utf8OrLocale bs =+ if isValidUTF8 bs then unpackPSFromUTF8 bs else decodeLocale bs++isValidUTF8 :: B.ByteString -> Bool isValidUTF8 = not . T.any (=='\xfffd') . decodeUtf8With lenientDecode packUnpackTest :: Test@@ -161,3 +172,18 @@ withUTF8OrNotPatchInfo :: (PatchInfo -> a) -> UTF8OrNotPatchInfo -> a withUTF8OrNotPatchInfo f mpi = case mpi of UTF8OrNotPatchInfo pinf -> f pinf++parseUnparseTest :: Test+parseUnparseTest = testProperty "parse . show == id" propParseUnparse++parsePatchInfo :: B.ByteString -> Maybe PatchInfo+parsePatchInfo = fmap fst . parseStrictly readPatchInfo++unparsePatchInfo :: PatchInfo -> B.ByteString+unparsePatchInfo = renderPS . showPatchInfo ForStorage++instance Arbitrary PatchInfo where+ arbitrary = arbitraryUnencodedPatchInfo++propParseUnparse :: PatchInfo -> Bool+propParseUnparse pi = Just pi == parsePatchInfo (unparsePatchInfo pi)
@@ -1,7 +1,10 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE StandaloneDeriving, GeneralizedNewtypeDeriving #-} module Darcs.Test.Patch.Properties.Check ( Check(..), checkAPatch ) where +import Prelude ()+import Darcs.Prelude import Control.Monad ( liftM )+import Data.Maybe ( isNothing ) import Darcs.Test.Patch.Check ( PatchCheck, checkMove, removeDir, createDir,@@ -16,17 +19,20 @@ import Darcs.Util.Path ( fn2fp ) import qualified Data.Map as M ( mapMaybe ) -import Darcs.Patch ( invert,- effect )+import Darcs.Patch ( invert, effect, PrimPatch ) import Darcs.Patch.Invert ( Invert )-import Darcs.Patch.V1 ()-import qualified Darcs.Patch.V1.Core as V1 ( RepoPatchV1(..) )+import Darcs.Patch.V1 ( )+import Darcs.Patch.V1.Core ( RepoPatchV1(..) )+import Darcs.Patch.V2.RepoPatch ( RepoPatchV2, isConsistent )+import qualified Darcs.Patch.V1.Prim as V1 ( Prim(..) )+import qualified Darcs.Patch.V2.Prim as V2 ( Prim(..) ) import Darcs.Patch.V1.Core ( isMerger )-import Darcs.Patch.Prim.V1 ()+import qualified Darcs.Patch.Prim.FileUUID as FileUUID ( Prim ) import Darcs.Patch.Prim.V1.Core ( Prim(..), DirPatchType(..), FilePatchType(..) ) import Darcs.Patch.Witnesses.Ordered -#include "impossible.h"+type Prim1 = V1.Prim+type Prim2 = V2.Prim class Check p where checkPatch :: p wX wY -> PatchCheck Bool@@ -35,16 +41,27 @@ checkPatch NilFL = isValid checkPatch (p :>: ps) = checkPatch p >> checkPatch ps +instance Check p => Check (p:>p) where+ checkPatch (p1 :> p2) = checkPatch p1 >> checkPatch p2+ checkAPatch :: (Invert p, Check p) => p wX wY -> Bool checkAPatch p = doCheck $ do _ <- checkPatch p checkPatch $ invert p -instance Check (V1.RepoPatchV1 Prim) where- checkPatch p | isMerger p = do- checkPatch $ effect p- checkPatch (V1.Merger _ _ _ _) = impossible- checkPatch (V1.Regrem _ _ _ _) = impossible- checkPatch (V1.PP p) = checkPatch p+instance PrimPatch prim => Check (RepoPatchV2 prim) where+ checkPatch p = return $ isNothing $ isConsistent p++instance Check (RepoPatchV1 Prim1) where+ checkPatch p | isMerger p = checkPatch $ effect p+ checkPatch (Merger _ _ _ _) = impossible+ checkPatch (Regrem _ _ _ _) = impossible+ checkPatch (PP p) = checkPatch p++deriving instance Check Prim1+deriving instance Check Prim2++instance Check FileUUID.Prim where+ checkPatch _ = return True -- XXX instance Check Prim where
@@ -15,10 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fno-warn-deprecations -fno-warn-orphans #-}-- module Darcs.Test.Patch.Properties.Generic ( invertSymmetry, inverseComposition, invertRollback, recommute, commuteInverses, effectPreserving,@@ -37,21 +33,22 @@ import Darcs.Test.Patch.Arbitrary.Generic ( Tree, flattenOne, MightBeEmptyHunk(..), MightHaveDuplicate(..) ) import Control.Monad ( msum )+ import Darcs.Patch.Witnesses.Show ( Show2(..), show2 )-import Darcs.Patch.Patchy ( Patchy, showPatch, commute, invert ) import Darcs.Patch.Read ( ReadPatch )-import Darcs.Patch.Show ( ShowPatchBasic )+import Darcs.Patch.Show+ ( ShowPatchBasic, displayPatch, showPatch, ShowPatchFor(ForStorage) ) import Darcs.Patch.Prim.Class ( PrimPatch, PrimOf, FromPrim ) import Darcs.Patch ()-import Darcs.Patch.Apply ( ApplyState )-import Darcs.Patch.Commute ( commuteFLorComplain )+import Darcs.Patch.Apply ( Apply, ApplyState )+import Darcs.Patch.Commute ( commute, commuteFL ) import Darcs.Patch.Merge ( Merge(merge) ) import Darcs.Patch.Read ( readPatch )-import Darcs.Patch.Invert ( invertFL )-import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) )+import Darcs.Patch.Invert ( Invert, invert, invertFL )+import Darcs.Patch.Witnesses.Eq ( Eq2(..), EqCheck(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), (:\/:)(..), (:/\:)(..), lengthFL, eqFL, reverseRL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), seal2, Sealed2 )-import Darcs.Util.Printer ( Doc, renderPS, redText, greenText, ($$), text, RenderMode(..) )+import Darcs.Util.Printer ( Doc, renderPS, redText, greenText, ($$), text ) --import Darcs.ColorPrinter ( traceDoc ) propIsMergeable :: forall model p wX . (FromPrim p, Merge p, RepoModel model)@@ -64,19 +61,19 @@ _ -> Nothing -- | invert symmetry inv(inv(p)) = p-invertSymmetry :: (Patchy p, MyEq p) => p wA wB -> TestResult+invertSymmetry :: (Invert p, Eq2 p) => p wA wB -> TestResult invertSymmetry p = case invert (invert p) =\/= p of IsEq -> succeeded NotEq -> failed $ redText "p /= inv(inv(p))" -inverseComposition :: (Patchy p, MyEq p) => (p :> p) wX wY -> TestResult+inverseComposition :: (Invert p, Eq2 p) => (p :> p) wX wY -> TestResult inverseComposition (a :> b) = case eqFL (reverseRL (invertFL (a:>:b:>:NilFL))) (invert b:>:invert a:>:NilFL) of IsEq -> succeeded NotEq -> failed $ redText "inv(a :>: b :>: NilFL) /= inv(b) :>: inv(a) :>: NilFL" -- | invert rollback if b = A(a) then a = A'(b)-invertRollback :: (ApplyState p ~ RepoState model, Patchy p, ShowPatchBasic p, RepoModel model)+invertRollback :: (Invert p, Apply p, ApplyState p ~ RepoState model, ShowPatchBasic p, RepoModel model) => WithState model p wA wB -> TestResult invertRollback (WithState a x b) = case maybeFail $ repoApply b (invert x) of@@ -86,10 +83,10 @@ else failed $ redText "a1: " $$ text (showModel a1) $$ redText " ---- is not equals to a:" $$ text (showModel a) $$ redText "where a was" $$ text (showModel b)- $$ redText "with (invert x) on top:" $$ showPatch (invert x)+ $$ redText "with (invert x) on top:" $$ displayPatch (invert x) -- | recommute AB ↔ B′A′ if and only if B′A′ ↔ AB-recommute :: (Patchy p, ShowPatchBasic p, MyEq p, MightHaveDuplicate p)+recommute :: (ShowPatchBasic p, Eq2 p, MightHaveDuplicate p) => (forall wX wY . ((p :> p) wX wY -> Maybe ((p :> p) wX wY))) -> (p :> p) wA wB -> TestResult recommute c (x :> y) =@@ -103,99 +100,150 @@ | hasDuplicate y' || hasDuplicate x' -> rejected | otherwise -> case c (y' :> x') of- Nothing -> failed (redText "failed" $$ showPatch y' $$ redText ":>" $$ showPatch x')+ Nothing -> failed (redText "failed, where x" $$ displayPatch x $$+ redText ":> y" $$ displayPatch y $$+ redText "y'" $$ displayPatch y' $$+ redText ":> x'" $$ displayPatch x') Just (x'' :> y'') -> case y'' =/\= y of- NotEq -> failed (redText "y'' =/\\= y failed, where x" $$ showPatch x $$- redText ":> y" $$ showPatch y $$- redText "y'" $$ showPatch y' $$- redText ":> x'" $$ showPatch x' $$- redText "x''" $$ showPatch x'' $$- redText ":> y''" $$ showPatch y'')+ NotEq -> failed (redText "y'' =/\\= y failed, where x" $$ displayPatch x $$+ redText ":> y" $$ displayPatch y $$+ redText "y'" $$ displayPatch y' $$+ redText ":> x'" $$ displayPatch x' $$+ redText "x''" $$ displayPatch x'' $$+ redText ":> y''" $$ displayPatch y'') IsEq -> case x'' =/\= x of- NotEq -> failed (redText "x'' /= x" $$ showPatch x'' $$ redText ":>" $$ showPatch y'')+ NotEq -> failed (+ redText "x'' /= x, where x" $$ displayPatch x $$+ redText ":> y" $$ displayPatch y $$+ redText "y'" $$ displayPatch y' $$+ redText ":> x'" $$ displayPatch x' $$+ redText "x''" $$ displayPatch x'' $$+ redText ":> y''" $$ displayPatch y'') IsEq -> succeeded -- | commuteInverses AB ↔ B′A′ if and only if B⁻¹A⁻¹ ↔ A′⁻¹B′⁻¹-commuteInverses :: (Patchy p, ShowPatchBasic p, MyEq p) => (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY))- -> (p :> p) wA wB -> TestResult+commuteInverses :: (Invert p, ShowPatchBasic p, Eq2 p)+ => (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY))+ -> (p :> p) wA wB -> TestResult commuteInverses c (x :> y) = case c (x :> y) of Nothing -> rejected Just (y' :> x') -> case c (invert y :> invert x) of Nothing -> failed $ redText "second commute failed" $$- redText "x" $$ showPatch x $$ redText "y" $$ showPatch y $$- redText "y'" $$ showPatch y' $$ redText "x'" $$ showPatch x'+ redText "x" $$ displayPatch x $$ redText "y" $$ displayPatch y $$+ redText "y'" $$ displayPatch y' $$ redText "x'" $$ displayPatch x' Just (ix' :> iy') -> case invert ix' =/\= x' of NotEq -> failed $ redText "invert ix' /= x'" $$- redText "x" $$ showPatch x $$- redText "y" $$ showPatch y $$- redText "y'" $$ showPatch y' $$- redText "x'" $$ showPatch x' $$- redText "ix'" $$ showPatch ix' $$- redText "iy'" $$ showPatch iy' $$- redText "invert ix'" $$ showPatch (invert ix') $$- redText "invert iy'" $$ showPatch (invert iy')+ redText "x" $$ displayPatch x $$+ redText "y" $$ displayPatch y $$+ redText "y'" $$ displayPatch y' $$+ redText "x'" $$ displayPatch x' $$+ redText "ix'" $$ displayPatch ix' $$+ redText "iy'" $$ displayPatch iy' $$+ redText "invert ix'" $$ displayPatch (invert ix') $$+ redText "invert iy'" $$ displayPatch (invert iy') IsEq -> case y' =\/= invert iy' of- NotEq -> failed $ redText "y' /= invert iy'" $$ showPatch iy' $$ showPatch y'+ NotEq -> failed $ redText "y' /= invert iy'" $$ displayPatch iy' $$ displayPatch y' IsEq -> succeeded -- | effect preserving AB <--> B'A' then effect(AB) = effect(B'A')-effectPreserving :: (Patchy p, MightBeEmptyHunk p, RepoModel model, ApplyState p ~ RepoState model) =>- (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY))- -> WithState model (p :> p) wA wB -> TestResult-effectPreserving _ (WithState _ (x :> _) _) | isEmptyHunk x = rejected-effectPreserving c (WithState r (x :> y) r')- = case c (x :> y) of- Nothing -> rejected+effectPreserving+ :: ( Apply p+ , MightBeEmptyHunk p+ , RepoModel model+ , ApplyState p ~ RepoState model+ , ShowPatchBasic p+ )+ => (forall wX wY. (p :> p) wX wY -> Maybe ((p :> p) wX wY))+ -> WithState model (p :> p) wA wB+ -> TestResult+effectPreserving _ (WithState _ (x :> _) _)+ | isEmptyHunk x = rejected+effectPreserving c (WithState r (x :> y) r') =+ case c (x :> y) of+ Nothing -> rejected Just (y' :> x') -> case maybeFail $ repoApply r y' of- Nothing -> failed $ redText "y' is not applicable to r."- Just r_y' ->- case maybeFail $ repoApply r_y' x' of- Nothing -> failed $ redText "x' is not applicable to r_y'."- Just r_y'x' -> if r_y'x' `eqModel` r'- then succeeded- else failed $ redText "r_y'x' is not equal to r'."+ Nothing ->+ failed+ $ redText "##x" $$ displayPatch x+ $$ redText "##y" $$ displayPatch y+ $$ redText "##y'" $$ displayPatch y'+ $$ redText "##x'" $$ displayPatch x'+ $$ redText "##y' is not applicable to r"+ $$ displayModel r+ Just r_y' ->+ case maybeFail $ repoApply r_y' x' of+ Nothing ->+ failed+ $ redText "##x" $$ displayPatch x+ $$ redText "##y" $$ displayPatch y+ $$ redText "##y'" $$ displayPatch y'+ $$ redText "##x'" $$ displayPatch x'+ $$ redText "##x' is not applicable to r_y'"+ $$ displayModel r_y'+ Just r_y'x' ->+ if r_y'x' `eqModel` r'+ then succeeded+ else+ failed+ $ redText "##x" $$ displayPatch x+ $$ redText "##y" $$ displayPatch y+ $$ redText "##y'" $$ displayPatch y'+ $$ redText "##x'" $$ displayPatch x'+ $$ redText "##r_y'x'"+ $$ displayModel r_y'x'+ $$ redText "##is not equal to r'"+ $$ displayModel r'+ where+ displayModel = text . showModel -- | patchAndInverseCommute If AB ↔ B′A′ then A⁻¹B′ ↔ BA′⁻¹-patchAndInverseCommute :: (Patchy p, ShowPatchBasic p, MyEq p) =>- (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY))- -> (p :> p) wA wB -> TestResult+patchAndInverseCommute+ :: (Invert p, ShowPatchBasic p, Eq2 p)+ => (forall wX wY. (p :> p) wX wY -> Maybe ((p :> p) wX wY))+ -> (p :> p) wA wB+ -> TestResult patchAndInverseCommute c (x :> y) = case c (x :> y) of- Nothing -> rejected- Just (y' :> x') ->- case c (invert x :> y') of- Nothing -> failed (redText ""- $$ redText "-------- original commute (x :> y):"- $$ showPatch x $$ redText ":>" $$ showPatch y- $$ redText "-------- result (y' :> x'):"- $$ showPatch y' $$ redText ":>" $$ showPatch x'- $$ redText "-------- bad commute (invert x :> y'):"- $$ showPatch (invert x) $$ redText ":>" $$ showPatch y')- Just (y'' :> ix') ->- case y'' =\/= y of- NotEq -> failed (redText "y'' /= y" $$- redText "x" $$ showPatch x $$- redText "y" $$ showPatch y $$- redText "x'" $$ showPatch x' $$- redText "y'" $$ showPatch y' $$- redText "y''" $$ showPatch y'' $$- redText ":> x'" $$ showPatch x')- IsEq -> case x' =\/= invert ix' of- NotEq -> failed (redText "x' /= invert ix'" $$- redText "y''" $$ showPatch y'' $$- redText ":> x'" $$ showPatch x' $$- redText "invert x" $$ showPatch (invert x) $$- redText ":> y" $$ showPatch y $$- redText "y'" $$ showPatch y' $$- redText "ix'" $$ showPatch ix')- IsEq -> succeeded+ Nothing -> rejected+ Just (y' :> x') ->+ case c (invert x :> y') of+ Nothing ->+ failed $+ redText "-------- original (x :> y)" $$+ displayPatch x $$ redText ":>" $$ displayPatch y $$+ redText "-------- result (y' :> x')" $$+ displayPatch y' $$ redText ":>" $$ displayPatch x' $$+ redText "-------- failed commute (invert x :> y')" $$+ displayPatch (invert x) $$ redText ":>" $$ displayPatch y'+ Just (y'' :> ix') ->+ case y'' =\/= y of+ NotEq ->+ failed $ redText "y'' /= y" $$+ redText "x" $$ displayPatch x $$+ redText "y" $$ displayPatch y $$+ redText "y'" $$ displayPatch y' $$+ redText "x'" $$ displayPatch x' $$+ redText "y''" $$ displayPatch y'' $$+ redText "ix'" $$ displayPatch ix'+ IsEq ->+ case x' =\/= invert ix' of+ NotEq ->+ failed $ redText "x' /= invert ix'" $$+ redText "x" $$ displayPatch x $$+ redText "y" $$ displayPatch y $$+ redText "y'" $$ displayPatch y' $$+ redText "x'" $$ displayPatch x' $$+ redText "invert x" $$ displayPatch (invert x) $$+ redText "y'" $$ displayPatch y' $$+ redText "invert ix'" $$ displayPatch (invert ix')+ IsEq -> succeeded -permutivity :: (Patchy p, ShowPatchBasic p, MyEq p) => (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY))+permutivity :: (ShowPatchBasic p, Eq2 p) => (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY)) -> (p :> p :> p) wA wB -> TestResult permutivity c (x:>y:>z) = case c (x :> y) of@@ -212,8 +260,8 @@ Just (z4 :> x4) -> --traceDoc (greenText "third commuted" $$ -- greenText "about to commute" $$- -- greenText "y1" $$ showPatch y1 $$- -- greenText "z4" $$ showPatch z4) $+ -- greenText "y1" $$ displayPatch y1 $$+ -- greenText "z4" $$ displayPatch z4) $ case c (y1 :> z4) of Nothing -> failed $ redText "permutivity2" Just (z3_ :> y4)@@ -221,68 +269,77 @@ --traceDoc (greenText "passed z3") $ error "foobar test" $ case c (y4 :> x4) of Nothing -> failed $ redText "permutivity5: input was" $$- redText "x" $$ showPatch x $$- redText "y" $$ showPatch y $$- redText "z" $$ showPatch z $$- redText "z3" $$ showPatch z3 $$+ redText "x" $$ displayPatch x $$+ redText "y" $$ displayPatch y $$+ redText "z" $$ displayPatch z $$+ redText "z3" $$ displayPatch z3 $$ redText "failed commute of" $$- redText "y4" $$ showPatch y4 $$- redText "x4" $$ showPatch x4 $$+ redText "y4" $$ displayPatch y4 $$+ redText "x4" $$ displayPatch x4 $$ redText "whereas commute of x and y give" $$- redText "y1" $$ showPatch y1 $$- redText "x1" $$ showPatch x1+ redText "y1" $$ displayPatch y1 $$+ redText "x1" $$ displayPatch x1 Just (x3_ :> y2_) | NotEq <- x3_ =\/= x3 -> failed $ redText "permutivity6" | NotEq <- y2_ =/\= y2 -> failed $ redText "permutivity7" | otherwise -> succeeded | otherwise -> failed $ redText "permutivity failed" $$- redText "z3" $$ showPatch z3 $$- redText "z3_" $$ showPatch z3_+ redText "z3" $$ displayPatch z3 $$+ redText "z3_" $$ displayPatch z3_ -partialPermutivity :: (Patchy p, ShowPatchBasic p) => (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY))- -> (p :> p :> p) wA wB -> TestResult-partialPermutivity c (xx:>yy:>zz) = pp (xx:>yy:>zz) <&&> pp (invert zz:>invert yy:>invert xx)- where pp (x:>y:>z) =- case c (y :> z) of+partialPermutivity+ :: (Invert p, ShowPatchBasic p)+ => (forall wX wY. (p :> p) wX wY -> Maybe ((p :> p) wX wY))+ -> (p :> p :> p) wA wB+ -> TestResult+partialPermutivity c (xx :> yy :> zz) =+ pp (xx :> yy :> zz) <&&> pp (invert zz :> invert yy :> invert xx)+ where+ pp (x :> y :> z) =+ case c (y :> z) of+ Nothing -> rejected+ Just (z1 :> y1) ->+ case c (x :> z1) of Nothing -> rejected- Just (z1 :> y1) ->- case c (x :> z1) of- Nothing -> rejected- Just (_ :> x1) ->- case c (x :> y) of- Just _ -> rejected -- this is covered by full permutivity test above- Nothing ->- case c (x1 :> y1) of- Nothing -> succeeded- Just _ -> failed $ greenText "partialPermutivity error" $$- greenText "x" $$ showPatch x $$- greenText "y" $$ showPatch y $$- greenText "z" $$ showPatch z+ Just (_ :> x1) ->+ case c (x :> y) of+ Just _ -> rejected -- this is covered by full permutivity test above+ Nothing ->+ case c (x1 :> y1) of+ Nothing -> succeeded+ Just _ ->+ failed $+ greenText "partialPermutivity error" $$ greenText "x" $$+ displayPatch x $$+ greenText "y" $$+ displayPatch y $$+ greenText "z" $$+ displayPatch z -mergeArgumentsConsistent :: (Patchy p, ShowPatchBasic p) =>+mergeArgumentsConsistent :: (ShowPatchBasic p) => (forall wX wY . p wX wY -> Maybe Doc) -> (p :\/: p) wA wB -> TestResult mergeArgumentsConsistent isConsistent (x :\/: y) = fromMaybe $- msum [(\z -> redText "mergeArgumentsConsistent x" $$ showPatch x $$ z) `fmap` isConsistent x,- (\z -> redText "mergeArgumentsConsistent y" $$ showPatch y $$ z) `fmap` isConsistent y]+ msum [(\z -> redText "mergeArgumentsConsistent x" $$ displayPatch x $$ z) `fmap` isConsistent x,+ (\z -> redText "mergeArgumentsConsistent y" $$ displayPatch y $$ z) `fmap` isConsistent y] -mergeConsistent :: (Patchy p, ShowPatchBasic p, Merge p) =>+mergeConsistent :: (ShowPatchBasic p, Merge p) => (forall wX wY . p wX wY -> Maybe Doc) -> (p :\/: p) wA wB -> TestResult mergeConsistent isConsistent (x :\/: y) = case merge (x :\/: y) of y' :/\: x' -> fromMaybe $- msum [(\z -> redText "mergeConsistent x" $$ showPatch x $$ z) `fmap` isConsistent x,- (\z -> redText "mergeConsistent y" $$ showPatch y $$ z) `fmap` isConsistent y,- (\z -> redText "mergeConsistent x'" $$ showPatch x' $$ z $$- redText "where x' comes from x" $$ showPatch x $$- redText "and y" $$ showPatch y) `fmap` isConsistent x',- (\z -> redText "mergeConsistent y'" $$ showPatch y' $$ z) `fmap` isConsistent y']+ msum [(\z -> redText "mergeConsistent x" $$ displayPatch x $$ z) `fmap` isConsistent x,+ (\z -> redText "mergeConsistent y" $$ displayPatch y $$ z) `fmap` isConsistent y,+ (\z -> redText "mergeConsistent x'" $$ displayPatch x' $$ z $$+ redText "where x' comes from x" $$ displayPatch x $$+ redText "and y" $$ displayPatch y) `fmap` isConsistent x',+ (\z -> redText "mergeConsistent y'" $$ displayPatch y' $$ z) `fmap` isConsistent y'] -mergeEitherWay :: (Patchy p, MyEq p, Merge p) => (p :\/: p) wX wY -> TestResult+mergeEitherWay :: (Eq2 p, Merge p) => (p :\/: p) wX wY -> TestResult mergeEitherWay (x :\/: y) = case merge (x :\/: y) of y' :/\: x' -> case merge (y :\/: x) of@@ -290,7 +347,7 @@ IsEq <- y'' =\/= y' -> succeeded | otherwise -> failed $ redText "mergeEitherWay bug" -mergeCommute :: (Patchy p, MyEq p, ShowPatchBasic p, Merge p, MightHaveDuplicate p)+mergeCommute :: (Eq2 p, ShowPatchBasic p, Merge p, MightHaveDuplicate p) => (p :\/: p) wX wY -> TestResult mergeCommute (x :\/: y) = case merge (x :\/: y) of@@ -303,36 +360,36 @@ | otherwise -> case commute (x :> y') of Nothing -> failed $ redText "mergeCommute 1" $$- redText "x" $$ showPatch x $$- redText "y" $$ showPatch y $$- redText "x'" $$ showPatch x' $$- redText "y'" $$ showPatch y'+ redText "x" $$ displayPatch x $$+ redText "y" $$ displayPatch y $$+ redText "x'" $$ displayPatch x' $$+ redText "y'" $$ displayPatch y' Just (y_ :> x'_) | IsEq <- y_ =\/= y, IsEq <- x'_ =\/= x' -> case commute (y :> x') of Nothing -> failed $ redText "mergeCommute 2 failed" $$- redText "x" $$ showPatch x $$- redText "y" $$ showPatch y $$- redText "x'" $$ showPatch x' $$- redText "y'" $$ showPatch y'+ redText "x" $$ displayPatch x $$+ redText "y" $$ displayPatch y $$+ redText "x'" $$ displayPatch x' $$+ redText "y'" $$ displayPatch y' Just (x_ :> y'_) | IsEq <- x_ =\/= x, IsEq <- y'_ =\/= y' -> succeeded | otherwise -> failed $ redText "mergeCommute 3" $$- redText "x" $$ showPatch x $$- redText "y" $$ showPatch y $$- redText "x'" $$ showPatch x' $$- redText "y'" $$ showPatch y' $$- redText "x_" $$ showPatch x_ $$- redText "y'_" $$ showPatch y'_+ redText "x" $$ displayPatch x $$+ redText "y" $$ displayPatch y $$+ redText "x'" $$ displayPatch x' $$+ redText "y'" $$ displayPatch y' $$+ redText "x_" $$ displayPatch x_ $$+ redText "y'_" $$ displayPatch y'_ | otherwise -> failed $ redText "mergeCommute 4" $$- redText "x" $$ showPatch x $$- redText "y" $$ showPatch y $$- redText "x'" $$ showPatch x' $$- redText "y'" $$ showPatch y' $$- redText "x'_" $$ showPatch x'_ $$- redText "y_" $$ showPatch y_+ redText "x" $$ displayPatch x $$+ redText "y" $$ displayPatch y $$+ redText "x'" $$ displayPatch x' $$+ redText "y'" $$ displayPatch y' $$+ redText "x'_" $$ displayPatch x'_ $$+ redText "y_" $$ displayPatch y_ -- | coalesce effect preserving@@ -345,9 +402,24 @@ Nothing -> rejected Just x -> case maybeFail $ repoApply r x of Nothing -> failed $ redText "x is not applicable to r."+ $$ text (showModel r)+ $$ displayPatch x+ $$ redText "a:>b"+ $$ displayPatch a $$ displayPatch b+ $$ redText "r'="+ $$ text (showModel r') Just r_x -> if r_x `eqModel` r' then succeeded- else failed $ redText "r_x /= r'"+ else failed $ redText "r_x /= r', r="+ $$ text (showModel r)+ $$ redText "a:>b="+ $$ displayPatch a $$ displayPatch b+ $$ redText "x="+ $$ displayPatch x+ $$ redText "r'="+ $$ text (showModel r')+ $$ redText "r_x="+ $$ text (showModel r_x) coalesceCommute :: (PrimPatch prim, MightBeEmptyHunk prim)@@ -358,29 +430,59 @@ case j (b :> c) of Nothing -> rejected Just x ->- case commuteFLorComplain (a :> b :>: c :>: NilFL) of- Right (b' :>: c' :>: NilFL :> a') ->- case commute (a:>:NilFL :> x) of- Just (x' :> a'':>:NilFL) ->- case a'' =/\= a' of- NotEq -> failed $ greenText "coalesceCommute 3"- IsEq -> case j (b' :> c') of- Nothing -> failed $ greenText "coalesceCommute 4"- Just x'' -> case x' =\/= x'' of- NotEq -> failed $ greenText "coalesceCommute 5"- IsEq -> succeeded- _ -> failed $ greenText "coalesceCommute 1"+ case commuteFL (a :> b :>: c :>: NilFL) of+ Just (b' :>: c' :>: NilFL :> a') ->+ case commuteFL (a :> x) of+ Just (x' :> a'') ->+ case a'' =/\= a' of+ NotEq ->+ failed $ greenText "a'' =/\\= a' failed"+ $$ display1+ $$ display2+ IsEq ->+ case j (b' :> c') of+ Nothing ->+ failed $ greenText "coalesce (b':>c') failed"+ $$ display1+ $$ display2+ Just x'' ->+ case x' =\/= x'' of+ NotEq ->+ failed $ greenText "x' =\\/= x'' failed"+ $$ display1+ $$ display2+ $$ display3+ IsEq -> succeeded+ where+ display3 = redText "## coalesce (b':>c') => x''"+ $$ displayPatch x''+ where+ display2 =+ redText "## commute (a:>x) => x'" $$ displayPatch x'+ $$ redText "## :> a''" $$ displayPatch a''+ _ -> failed $ greenText "commute a x failed" $$ display1+ where+ display1 =+ redText "## a" $$ displayPatch a+ $$ redText "## b" $$ displayPatch b+ $$ redText "## c" $$ displayPatch c+ $$ redText "## coalesce (b:>c) => x" $$ displayPatch x+ $$ redText "## commute (a:>b:>c) => a'" $$ displayPatch a'+ $$ redText "## b'" $$ displayPatch b'+ $$ redText "## c'" $$ displayPatch c' _ -> rejected -show_read :: (Show2 p, MyEq p, ReadPatch p, ShowPatchBasic p, Patchy p) => p wA wB -> TestResult-show_read p = let ps = renderPS Standard (showPatch p)+-- note: we would normally use displayPatch in the failure message+-- but that would be very misleading here+show_read :: (Show2 p, Eq2 p, ReadPatch p, ShowPatchBasic p) => p wA wB -> TestResult+show_read p = let ps = renderPS (showPatch ForStorage p) in case readPatch ps of- Nothing -> failed (redText "unable to read " $$ showPatch p)+ Nothing -> failed (redText "unable to read " $$ showPatch ForStorage p) Just (Sealed p' ) | IsEq <- p' =\/= p -> succeeded | otherwise -> failed $ redText "trouble reading patch p" $$- showPatch p $$+ showPatch ForStorage p $$ redText "reads as p'" $$- showPatch p' $$+ showPatch ForStorage p' $$ redText "aka" $$ greenText (show2 p) $$ redText "and" $$
@@ -14,84 +14,82 @@ import Darcs.Test.Patch.WSub import Darcs.Test.Util.TestResult -import Darcs.Patch.Prim.V1 ( Prim )-import Darcs.Patch.Patchy ( showPatch )+import Darcs.Patch.Invert ( Invert ) import Darcs.Patch.Read ( ReadPatch )-import Darcs.Patch.Show ( ShowPatchBasic )+import Darcs.Patch.Show ( ShowPatchBasic, displayPatch ) import Darcs.Patch.Witnesses.Show import Darcs.Patch.Witnesses.Eq import Darcs.Patch.Witnesses.Sealed( Sealed ) import Darcs.Patch.Merge ( Merge )-import Darcs.Patch ( Patchy ) import Darcs.Util.Printer ( Doc, redText, ($$) ) import qualified Darcs.Util.Tree as T ( Tree ) -permutivity :: (Patchy wp, ShowPatchBasic wp, MyEq wp, WSub wp p)+permutivity :: (ShowPatchBasic wp, Eq2 wp, WSub wp p) => (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY)) -> (p :> p :> p) wA wB -> TestResult permutivity f = W.permutivity (fmap toW . f . fromW) . toW -partialPermutivity :: (Patchy wp, ShowPatchBasic wp, MyEq wp, WSub wp p)+partialPermutivity :: (Invert wp, ShowPatchBasic wp, Eq2 wp, WSub wp p) => (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY)) -> (p :> p :> p) wA wB -> TestResult partialPermutivity f = W.partialPermutivity (fmap toW . f . fromW) . toW -mergeEitherWay :: (Patchy wp, ShowPatchBasic wp, MyEq wp, Merge wp, WSub wp p) => (p :\/: p) wX wY -> TestResult+mergeEitherWay :: (ShowPatchBasic wp, Eq2 wp, Merge wp, WSub wp p) => (p :\/: p) wX wY -> TestResult mergeEitherWay = W.mergeEitherWay . toW -commuteInverses :: (Patchy wp, ShowPatchBasic wp, MyEq wp, WSub wp p)+commuteInverses :: (Invert wp, ShowPatchBasic wp, Eq2 wp, WSub wp p) => (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY)) -> (p :> p) wA wB -> TestResult commuteInverses f = W.commuteInverses (fmap toW . f . fromW) . toW -recommute :: (Patchy wp, ShowPatchBasic wp, MightHaveDuplicate wp, MyEq wp, WSub wp p)+recommute :: (ShowPatchBasic wp, MightHaveDuplicate wp, Eq2 wp, WSub wp p) => (forall wX wY . ((p :> p) wX wY -> Maybe ((p :> p) wX wY))) -> (p :> p) wA wB -> TestResult recommute f = W.recommute (fmap toW . f . fromW) . toW -mergeCommute :: (Patchy wp, MightHaveDuplicate wp, ShowPatchBasic wp, MyEq wp, Merge wp, WSub wp p)+mergeCommute :: (MightHaveDuplicate wp, ShowPatchBasic wp, Eq2 wp, Merge wp, WSub wp p) => (p :\/: p) wX wY -> TestResult mergeCommute = W.mergeCommute . toW -mergeConsistent :: (Patchy wp, Merge wp, ShowPatchBasic wp, WSub wp p) =>+mergeConsistent :: (Merge wp, ShowPatchBasic wp, WSub wp p) => (forall wX wY . p wX wY -> Maybe Doc) -> (p :\/: p) wA wB -> TestResult mergeConsistent f = W.mergeConsistent (f . fromW) . toW -mergeArgumentsConsistent :: (Patchy wp, ShowPatchBasic wp, WSub wp p) =>+mergeArgumentsConsistent :: (ShowPatchBasic wp, WSub wp p) => (forall wX wY . p wX wY -> Maybe Doc) -> (p :\/: p) wA wB -> TestResult mergeArgumentsConsistent f = W.mergeArgumentsConsistent (f . fromW) . toW -show_read :: (Patchy p, ShowPatchBasic p, ReadPatch p, MyEq p, Show2 p) => p wX wY -> TestResult+show_read :: (ShowPatchBasic p, ReadPatch p, Eq2 p, Show2 p) => p wX wY -> TestResult show_read = W.show_read -patchAndInverseCommute :: (Patchy wp, ShowPatchBasic wp, MyEq wp, WSub wp p) =>+patchAndInverseCommute :: (Invert wp, ShowPatchBasic wp, Eq2 wp, WSub wp p) => (forall wX wY . (p :> p) wX wY -> Maybe ((p :> p) wX wY)) -> (p :> p) wA wB -> TestResult patchAndInverseCommute f = W.patchAndInverseCommute (fmap toW . f . fromW) . toW -coalesceCommute :: MightBeEmptyHunk Prim- => (forall wX wY . (Prim :> Prim) wX wY -> Maybe (FL Prim wX wY))- -> (Prim :> Prim :> Prim) wA wB -> TestResult+coalesceCommute :: MightBeEmptyHunk Prim2+ => (forall wX wY . (Prim2 :> Prim2) wX wY -> Maybe (FL Prim2 wX wY))+ -> (Prim2 :> Prim2 :> Prim2) wA wB -> TestResult coalesceCommute f = W.coalesceCommute (fmap toW . f . fromW) . toW consistentTreeFlattenings :: (RepoState model ~ T.Tree, RepoModel model)- => Sealed (WithStartState model (Tree Prim)) -> TestResult+ => Sealed (WithStartState model (Tree Prim2)) -> TestResult consistentTreeFlattenings = (\x -> if W.propConsistentTreeFlattenings x then succeeded else failed $ redText "oops") -commuteFails :: (MyEq p, Patchy p, ShowPatchBasic p)+commuteFails :: (Eq2 p, ShowPatchBasic p) => ((p :> p) wX wY -> Maybe ((p :> p) wX wY)) -> (p :> p) wX wY -> TestResult commuteFails c (x :> y) = case c (x :> y) of Nothing -> succeeded Just (y' :> x') ->- failed $ redText "x" $$ showPatch x $$- redText ":> y" $$ showPatch y $$- redText "y'" $$ showPatch y' $$- redText ":> x'" $$ showPatch x'+ failed $ redText "x" $$ displayPatch x $$+ redText ":> y" $$ displayPatch y $$+ redText "y'" $$ displayPatch y' $$+ redText ":> x'" $$ displayPatch x'
@@ -1,19 +1,25 @@-{-# LANGUAGE CPP #-} module Darcs.Test.Patch.Properties.RepoPatchV2 ( propConsistentTreeFlattenings ) where +import Prelude ()+import Darcs.Prelude+ 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 )+import Darcs.Patch.Prim ( fromPrim )+import Darcs.Patch.Witnesses.Sealed ( Sealed(..) )+import qualified Darcs.Patch.V2.Prim as V2 ( Prim )+import Darcs.Patch.V2 ( RepoPatchV2 ) -#include "impossible.h"+type Prim2 = V2.Prim +fromPrim2 :: Prim2 wX wY -> RepoPatchV2 Prim2 wX wY+fromPrim2 = fromPrim+ 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@@ -26,10 +32,11 @@ | otherwise = "Nothing" propConsistentTreeFlattenings :: (RepoState model ~ T.Tree, RepoModel model)- => Sealed (WithStartState model (Tree Prim)) -> Bool+ => Sealed (WithStartState model (Tree Prim2))+ -> Bool propConsistentTreeFlattenings (Sealed (WithStartState start t)) = fromJust $- do Sealed (G2 flat) <- return $ flattenTree $ mapTree prim2repopatchV2 t+ do Sealed (G2 flat) <- return $ flattenTree $ mapTree fromPrim2 t rms <- return $ map (start `repoApply`) flat return $ and $ zipWith assertEqualFst (zip rms flat) (tail $ zip rms flat)
@@ -1,5 +1,3 @@-{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Test.Patch.Properties.V1Set1 ( checkMerge, checkMergeEquiv, checkMergeSwap, checkCanon , checkCommute, checkCantCommute@@ -7,16 +5,17 @@ , tMergeEitherWayValid, tTestCheck ) where import Darcs.Patch- ( Patchy, commute, invert, merge, effect+ ( commute, invert, merge, effect , readPatch, showPatch , fromPrim, canonize, sortCoalesceFL )-import Darcs.Patch.Prim.V1 ( Prim )+import Darcs.Patch.Invert ( Invert ) import Darcs.Patch.Merge ( Merge ) import Darcs.Patch.Read ( ReadPatch )-import Darcs.Patch.Show ( ShowPatchBasic )+import Darcs.Patch.Show ( ShowPatchBasic, ShowPatchFor(..) ) import qualified Darcs.Patch.V1 as V1 ( RepoPatchV1 )+import qualified Darcs.Patch.V1.Prim as V1 ( Prim(..) ) import Darcs.Test.Patch.Properties.Check ( checkAPatch, Check )-import Darcs.Util.Printer ( renderPS, RenderMode(..) )+import Darcs.Util.Printer ( renderPS ) import Darcs.Patch.Witnesses.Eq import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Witnesses.Show@@ -27,14 +26,14 @@ import Darcs.Util.Printer ( text ) -type Patch = V1.RepoPatchV1 Prim+type Patch = V1.RepoPatchV1 V1.Prim -quickmerge :: (Patchy p, Merge p) => (p :\/: p ) wX wY -> p wY wZ+quickmerge :: (Merge p) => (p :\/: p ) wX wY -> p wY wZ quickmerge (p1:\/:p2) = case merge (p1:\/:p2) of _ :/\: p1' -> unsafeCoercePEnd p1' -instance MyEq p => Eq ((p :/\: p) wX wY) where+instance Eq2 p => Eq ((p :/\: p) wX wY) where (x :/\: y) == (x' :/\: y') = isIsEq (x =\/= x') && isIsEq (y =\/= y') -- ----------------------------------------------------------------------------@@ -130,15 +129,15 @@ -- A few "test" properties, doing things with input patches and giving a OK/not -- OK type of answer. -tShowRead :: (Show2 p, Patchy p, ReadPatch p, ShowPatchBasic p)+tShowRead :: (Show2 p, ReadPatch p, ShowPatchBasic p) => (forall wX wY wW wZ . p wX wY -> p wW wZ -> Bool) -> forall wX wY . p wX wY -> TestResult tShowRead eq p =- case readPatch $ renderPS Standard $ showPatch p of+ case readPatch $ renderPS $ showPatch ForStorage p of Just (Sealed p') -> if p' `eq` p then succeeded else failed $ text $ "Failed to read shown: "++(show2 p)++"\n" Nothing -> failed $ text $ "Failed to read at all: "++(show2 p)++"\n" -tMergeEitherWayValid :: forall wX wY p . (Check p, Show2 p, Merge p, Patchy p) => (p :\/: p) wX wY -> TestResult+tMergeEitherWayValid :: forall wX wY p . (Check p, Show2 p, Merge p, Invert p) => (p :\/: p) wX wY -> TestResult tMergeEitherWayValid (p1 :\/: p2) = case p2 :>: quickmerge (p1:\/: p2) :>: NilFL of combo2 ->
@@ -15,9 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}-- module Darcs.Test.Patch.Properties.V1Set2 ( propCommuteInverse, propPatchAndInverseIsIdentity , propSimpleSmartMergeGoodEnough, propCommuteEquivalency@@ -36,7 +33,9 @@ , propUnravelOrderIndependent, propResolveConflictsValid ) where -import Prelude hiding ( pi )+import Prelude ()+import Darcs.Prelude+ import Test.QuickCheck import Test.Framework.Providers.QuickCheck2 ( testProperty ) import Test.Framework ( Test )@@ -46,24 +45,29 @@ import Darcs.Patch ( invert, commute, merge, readPatch, resolveConflicts,- fromPrim, showPatch )+ fromPrim, showPatch, ShowPatchFor(..) ) import Darcs.Patch.Commute ( Commute ) import Darcs.Patch.Invert ( Invert ) import qualified Darcs.Patch.V1 as V1 ( RepoPatchV1 )+import qualified Darcs.Patch.V1.Prim as V1 ( Prim(..) ) import Darcs.Patch.V1.Commute ( unravel, merger )-import Darcs.Patch.Prim.V1 ()-import Darcs.Patch.Prim.V1.Core ( Prim(..) )-import Darcs.Patch.Prim.V1.Commute ( WrappedCommuteFunction(..), Perhaps(..),- subcommutes )-import Darcs.Util.Printer ( renderPS, RenderMode(..) )+import Darcs.Patch.Prim.V1 ( Prim )+import Darcs.Patch.Prim.V1.Commute+ ( Perhaps(..)+ , toPerhaps+ , speedyCommute+ , cleverCommute+ , commuteFiledir+ , commuteFilepatches+ )+import Darcs.Util.Printer ( renderPS ) import Darcs.Patch.Witnesses.Eq import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), unsafeUnseal, unseal, mapSeal, Sealed2(..) ) import Darcs.Patch.Witnesses.Unsafe -#include "impossible.h"--type Patch = V1.RepoPatchV1 Prim+type Prim1 = V1.Prim+type Patch = V1.RepoPatchV1 Prim1 -- | Groups a set of tests by giving them the same prefix in their description.@@ -84,7 +88,7 @@ propCommuteTwice :: Sealed2 (FL Patch :> FL Patch) -> Property propCommuteTwice (Sealed2 (p1:>p2)) = (doesCommute p1 p2) ==> (Just (p1:>p2) == (commute (p1:>p2) >>= commute))-doesCommute :: (MyEq p, Invert p, Commute p, Check p) => p wX wY -> p wY wZ -> Bool+doesCommute :: (Eq2 p, Invert p, Commute p, Check p) => p wX wY -> p wY wZ -> Bool doesCommute p1 p2 = commute (p1:>p2) /= Nothing && checkAPatch (p1:>:p2:>:NilFL) propCommuteEquivalency :: Sealed2 (FL Patch :> FL Patch) -> Property@@ -208,9 +212,9 @@ and $ map (\l -> (\ml -> checkAPatch (p+>+ml)) `unseal` mergeList l) $ resolveConflicts p -mergeList :: [Sealed (FL Prim wX)] -> Sealed (FL Patch wX)+mergeList :: [Sealed (FL Prim1 wX)] -> Sealed (FL Patch wX) mergeList patches = mapFL_FL fromPrim `mapSeal` doml NilFL patches- where doml :: FL Prim wX wY -> [Sealed (FL Prim wX)] -> Sealed (FL Prim wX)+ where doml :: FL Prim1 wX wY -> [Sealed (FL Prim1 wX)] -> Sealed (FL Prim1 wX) doml mp (Sealed p:ps) = case commute (invert p :> mp) of Just (mp' :> _) -> doml (p +>+ mp') ps@@ -218,7 +222,7 @@ doml mp [] = Sealed mp propReadShow :: FL Patch wX wY -> Bool-propReadShow p = case readPatch $ renderPS Standard $ showPatch p of+propReadShow p = case readPatch $ renderPS $ showPatch ForStorage p of Just (Sealed p') -> isIsEq (p' =\/= p) Nothing -> False @@ -234,8 +238,29 @@ Nothing -> False Just (_ :> p1'') -> isIsEq (p1'' =/\= p1) -type CommuteProperty = Sealed2 (Prim :> Prim) -> Property+type CommuteProperty = Sealed2 (Prim1 :> Prim1) -> Property +type CommuteFunction =+ forall wX wY . (Prim1 :> Prim1) wX wY -> Perhaps ((Prim1 :> Prim1) wX wY)++newtype WrappedCommuteFunction = WrappedCommuteFunction+ { runWrappedCommuteFunction :: CommuteFunction }++wrapCommuteFunction :: (forall wX wY . (Prim :> Prim) wX wY -> Perhaps ((Prim :> Prim) wX wY)) -> WrappedCommuteFunction+wrapCommuteFunction f = WrappedCommuteFunction $+ \(p :> q) -> do+ q' :> p' <- f (V1.unPrim p :> V1.unPrim q)+ return (V1.Prim q' :> V1.Prim p')++subcommutes :: [(String, WrappedCommuteFunction)]+subcommutes =+ [("speedyCommute", wrapCommuteFunction speedyCommute),+ ("commuteFiledir", wrapCommuteFunction (cleverCommute commuteFiledir)),+ ("commuteFilepatches", wrapCommuteFunction (cleverCommute commuteFilepatches)),+ ("commutex", wrapCommuteFunction (toPerhaps . commute))+ ]++ subcommutesInverse :: [(String, CommuteProperty)] subcommutesInverse = zip names (map prop_subcommute cs) where (names, cs) = unzip subcommutes@@ -297,19 +322,19 @@ Failed -> True _ -> False -doesFail :: WrappedCommuteFunction -> Prim wX wY -> Prim wY wZ -> Bool+doesFail :: WrappedCommuteFunction -> Prim1 wX wY -> Prim1 wY wZ -> Bool doesFail c p1 p2 = 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 :: WrappedCommuteFunction -> Prim1 wX wY -> Prim1 wY wZ -> Bool does c p1 p2 = 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 :: WrappedCommuteFunction -> Prim1 wX wY -> Prim1 wY wZ -> Bool nontrivial c p1 p2 = succeeds (runWrappedCommuteFunction c (p1 :> p2)) && checkAPatch (p1 :>: p2 :>: NilFL) where succeeds (Succeeded (p2' :> p1' )) = not (p1' `unsafeCompare` p1 && p2' `unsafeCompare` p2)
@@ -1,5 +1,6 @@ module Darcs.Test.Patch.RepoModel where import Darcs.Patch.Apply ( Apply, ApplyState )+import Darcs.Patch.Witnesses.Ordered ( FL ) import Test.QuickCheck ( Gen ) type Fail = Either String@@ -21,3 +22,4 @@ type family ModelOf (patch :: * -> * -> *) :: * -> * +type instance ModelOf (FL prim) = ModelOf prim
@@ -8,8 +8,8 @@ import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) -import Darcs.Patch.Prim.V1 ( Prim ) import Darcs.Patch.V2 ( RepoPatchV2 )+import qualified Darcs.Patch.V2.Prim as V2 import Darcs.Patch.RepoType ( RepoType(..), RebaseType(..) ) import Darcs.UI.SelectChanges@@ -26,7 +26,7 @@ -- A test module for interactive patch selection. -type Patch = RepoPatchV2 Prim+type Patch = RepoPatchV2 V2.Prim testSuite :: Test testSuite = testGroup "Darcs.Patch.Selection" $
@@ -1,6 +1,3 @@-{-# LANGUAGE CPP #-}-- -- | Repository model module Darcs.Test.Patch.V1Model ( V1Model, repoTree@@ -44,14 +41,12 @@ 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 ) import qualified Data.Map as M import Test.QuickCheck ( Arbitrary(..) , Gen, choose, vectorOf, frequency ) -#include "impossible.h"-+-- import Text.Show.Pretty ( ppShow ) ---------------------------------------------------------------------- -- * Model definition@@ -76,17 +71,18 @@ ---------------------------------------- -- Instances +data FlatItem = Dir FilePath | File FilePath [String]+ deriving Show++flattenTree :: Tree Fail -> [FlatItem]+flattenTree = map flattenEntry . T.list where+ flattenEntry (fn, T.SubTree _) = Dir (BC.unpack (flatten fn))+ flattenEntry (fn, T.File blob) = File (BC.unpack (flatten fn))+ (map BLC.unpack $ BLC.lines $ unFail $ T.readBlob blob)+ flattenEntry (_, _) = impossible+ instance Show (V1Model wX) where- show repo = "V1Model{ "- ++ intercalate " " (map showEntry $ list repo)- ++ " }"- where- showPath = show . flatten- showContent content = "[" ++ intercalate " " (map show content) ++ "]"- showEntry (path,item)- | isDir item = showPath path- | isFile item = showPath path ++ showContent (fileContent item)- showEntry _ = impossible+ show repo = "V1Model " ++ show (flattenTree (repoTree repo)) instance Show1 V1Model where showDict1 = ShowDictClass@@ -276,7 +272,7 @@ -- Small repositories help generating (potentially) conflicting patches. instance RepoModel V1Model where type RepoState V1Model = Tree- showModel m = show m+ showModel m = show {- ppShow -} m aSmallRepo = do filesNo <- frequency [(3, return 1), (1, return 2)] dirsNo <- frequency [(3, return 1), (1, return 0)] aRepo filesNo dirsNo
@@ -30,11 +30,14 @@ import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP, unsafeCoercePStart, unsafeCoercePEnd ) import Darcs.Patch.Merge ( Merge )-import Darcs.Patch.Prim.V1 ( Prim ) import Darcs.Patch.V2 ( RepoPatchV2 )-import Darcs.Patch.Patchy ( Commute, Invert(..) )+import qualified Darcs.Patch.V2.Prim as V2+import Darcs.Patch.Commute ( Commute )+import Darcs.Patch.Invert ( Invert(..) ) +type Prim2 = V2.Prim + infixr 5 :>: infixr 5 +>+ infixr 1 :>@@ -85,7 +88,7 @@ fromW = id toW = id -instance WSub Prim Prim where+instance WSub Prim2 Prim2 where fromW = id toW = id @@ -107,7 +110,7 @@ instance (WSub wp p, Show2 wp) => Show2 (FL p) where showDict2 = ShowDictClass -instance (WSub wp p, Commute wp, MyEq wp) => MyEq (FL p) where+instance (WSub wp p, Commute wp, Eq2 wp) => Eq2 (FL p) where unsafeCompare x y = unsafeCompare (toW x) (toW y) instance (WSub wp p, Commute wp, Invert wp) => Invert (FL p) where@@ -126,12 +129,12 @@ commute :: (WSub wp p, Commute wp) => (p :> p) wX wY -> Maybe ((p :> p) wX wY) commute = fmap fromW . W.commute . toW -getPairs :: FL (RepoPatchV2 Prim) wX wY -> [Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim)]+getPairs :: FL (RepoPatchV2 Prim2) wX wY -> [Sealed2 (RepoPatchV2 Prim2 :> RepoPatchV2 Prim2)] getPairs = map (mapSeal2 fromW) . W.getPairs . toW -getTriples :: FL (RepoPatchV2 Prim) wX wY -> [Sealed2 (RepoPatchV2 Prim :> RepoPatchV2 Prim :> RepoPatchV2 Prim)]+getTriples :: FL (RepoPatchV2 Prim2) wX wY -> [Sealed2 (RepoPatchV2 Prim2 :> RepoPatchV2 Prim2 :> RepoPatchV2 Prim2)] getTriples = map (mapSeal2 fromW) . W.getTriples . toW -coalesce :: (Prim :> Prim) wX wY -> Maybe (FL Prim wX wY)+coalesce :: (Prim2 :> Prim2) wX wY -> Maybe (FL Prim2 wX wY) coalesce = fmap fromW . W.coalesce . toW
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, MultiParamTypeClasses, ScopedTypeVariables, UndecidableInstances #-}+{-# LANGUAGE MultiParamTypeClasses, ScopedTypeVariables, UndecidableInstances #-} module Darcs.Test.Patch.WithState
@@ -0,0 +1,159 @@+module Darcs.Test.Repository.Inventory where++import Darcs.Repository.Inventory+ ( Inventory(..)+ , HeadInventory+ , ValidHash(..)+ , InventoryHash+ , PatchHash+ , PristineHash+ , getValidHash+ , mkValidHash+ , parseInventory+ , showInventory+ , skipPristineHash+ , peekPristineHash+ , pokePristineHash+ , prop_inventoryParseShow+ , prop_peekPokePristineHash+ , prop_skipPokePristineHash+ )+import Darcs.Patch.Info ( rawPatchInfo )+import Darcs.Util.Printer ( renderPS )++import Darcs.Test.Patch.Info ()++import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import Test.Framework ( Test, testGroup )+import Test.Framework.Providers.HUnit ( testCase )+import Test.Framework.Providers.QuickCheck2 ( testProperty )+import Test.HUnit ( Assertion, (@=?) )+import Test.QuickCheck++testSuite :: Test+testSuite = testGroup "Darcs.Repository.Inventory"+ [ testProperty "parse/show roundtrips" prop_inventoryParseShow+ , testProperty "peek gets back what we poked" prop_peekPokePristineHash+ , testProperty "skip/poke roundtrips" prop_skipPokePristineHash+ , testCase "example1" (testInventory rawHeadInv1 headInv1)+ , testCase "example2" (testInventory rawHeadInv2 headInv2)+ ]++instance Arbitrary B.ByteString where+ arbitrary = B.pack <$> arbitrary++instance Arbitrary Inventory where+ arbitrary = uncurry Inventory <$> arbitrary++instance Arbitrary InventoryHash where+ arbitrary = arbitraryHash+instance Arbitrary PatchHash where+ arbitrary = arbitraryHash+instance Arbitrary PristineHash where+ arbitrary = arbitraryHash++arbitraryHash :: ValidHash h => Gen h+arbitraryHash = mkValidHash <$> do+ n <- elements [64, 75] -- see D.R.Cache.okayHash+ vectorOf n $ elements $ '-' : (['0'..'9'] ++ ['a'..'f'])++testInventory :: B.ByteString -> HeadInventory -> Assertion+testInventory raw (vhash,inv) = do+ let hash = getValidHash vhash+ hash @=? peekPristineHash raw+ let rest = skipPristineHash raw+ Just inv @=? parseInventory rest+ rest @=? renderPS (showInventory inv)+ raw @=? renderPS (pokePristineHash hash rest)++headInv1 :: HeadInventory+headInv1 =+ ( mkValidHash "57fb9c1abbed1c0b880e2fffebe32a2163762b87e67e9bf4dcd3168e5abcad83"+ , Inventory+ { inventoryParent = Nothing+ , inventoryPatches =+ [ ( rawPatchInfo+ "20180311141206"+ "Add d/f and e."+ "tester"+ [ "Ignore-this: b541ff7ea385297c8ad07fe58016efa8" ]+ False+ , mkValidHash+ "0000000154-703d7811c2e3f1e1aa81e4be5fab31a291cc18158ec8a75733b6faa5fb406286"+ )+ , ( rawPatchInfo+ "20180311141206"+ "Move d/f to e/f."+ "tester"+ [ "Ignore-this: b71452c8a91c573f7e7fa2e8eb34afd1" ]+ False+ , mkValidHash+ "0000000106-4b1bc6db02d2eea04efe888b64ce853a416c14ae1ae43550b0137f11a8a8dfee"+ )+ ]+ }+ )++rawHeadInv1 :: B.ByteString+rawHeadInv1 = BC.pack+ "pristine:57fb9c1abbed1c0b880e2fffebe32a2163762b87e67e9bf4dcd3168e5abcad83\n\+ \[Add d/f and e.\n\+ \tester**20180311141206\n\+ \ Ignore-this: b541ff7ea385297c8ad07fe58016efa8\n\+ \] \n\+ \hash: 0000000154-703d7811c2e3f1e1aa81e4be5fab31a291cc18158ec8a75733b6faa5fb406286\n\+ \[Move d/f to e/f.\n\+ \tester**20180311141206\n\+ \ Ignore-this: b71452c8a91c573f7e7fa2e8eb34afd1\n\+ \] \n\+ \hash: 0000000106-4b1bc6db02d2eea04efe888b64ce853a416c14ae1ae43550b0137f11a8a8dfee\n\+ \"++headInv2 :: HeadInventory+headInv2 =+ ( mkValidHash "f2f70f1326252fc53077d7cd71769f405618829ba40a8f00f112ac97213f5f4b"+ , Inventory+ { inventoryParent =+ Just+ (mkValidHash+ "0000220070-6ef010a955c38fc4301787092979994bafd366eb50152b66e089deff649d35da")+ , inventoryPatches =+ [ ( rawPatchInfo+ "20160429142058"+ "TAG 2.12.0"+ "Guillaume Hoffmann <guillaumh@gmail.com>"+ [ "Ignore-this: 5c8cbe0424942686a2168f9e6fd8e35d" ]+ False+ , mkValidHash+ "0000088075-e1cc4489099cfff1df5875a8146dc012110c156f3dad839f4632d62ee2331e43"+ )+ , ( rawPatchInfo+ "20160429143015"+ "bump version to 2.13.0"+ "Guillaume Hoffmann <guillaumh@gmail.com>"+ [ "Ignore-this: 7468e30e96f3bf833f4e374e9cc7e515" ]+ False+ , mkValidHash+ "0000000198-0f5455b7c229e132a2fc2173dcce2b567f806c1d3eb1c37fd9fe8d8e42ef4fc9"+ )+ ]+ }+ )++rawHeadInv2 :: B.ByteString+rawHeadInv2 = BC.pack+ "pristine:f2f70f1326252fc53077d7cd71769f405618829ba40a8f00f112ac97213f5f4b\n\+ \Starting with inventory:\n\+ \0000220070-6ef010a955c38fc4301787092979994bafd366eb50152b66e089deff649d35da\n\+ \[TAG 2.12.0\n\+ \Guillaume Hoffmann <guillaumh@gmail.com>**20160429142058\n\+ \ Ignore-this: 5c8cbe0424942686a2168f9e6fd8e35d\n\+ \] \n\+ \hash: 0000088075-e1cc4489099cfff1df5875a8146dc012110c156f3dad839f4632d62ee2331e43\n\+ \[bump version to 2.13.0\n\+ \Guillaume Hoffmann <guillaumh@gmail.com>**20160429143015\n\+ \ Ignore-this: 7468e30e96f3bf833f4e374e9cc7e515\n\+ \] \n\+ \hash: 0000000198-0f5455b7c229e132a2fc2173dcce2b567f806c1d3eb1c37fd9fe8d8e42ef4fc9\n\+ \"
@@ -1,4 +1,3 @@- module Darcs.Test.Util.TestResult ( TestResult , succeeded@@ -8,56 +7,51 @@ , fromMaybe , isOk , isFailed- )- where-+ ) where -import Darcs.Util.Printer ( Doc, renderString, RenderMode(..) )+import Darcs.Util.Printer (Doc, renderString) import qualified Test.QuickCheck.Property as Q ---data TestResult = TestSucceeded- | TestFailed Doc- | TestRejected -- ^ Rejects test case-+data TestResult+ = TestSucceeded+ | TestFailed Doc+ | TestRejected succeeded :: TestResult succeeded = TestSucceeded -failed :: Doc -- ^ Error message- -> TestResult+failed :: Doc -> TestResult failed = TestFailed rejected :: TestResult rejected = TestRejected --- | @t <&&> s@ fails <=> t or s fails--- @t <&&> s@ succeeds <=> none fails and some succeeds--- @t <&&> s@ is rejected <=> both are rejected+-- | Succeed even if one of the arguments is rejected. (<&&>) :: TestResult -> TestResult -> TestResult-t@(TestFailed _) <&&> _s = t-_t <&&> s@(TestFailed _) = s-TestRejected <&&> s = s-t <&&> TestRejected = t-TestSucceeded <&&> TestSucceeded = TestSucceeded+t@(TestFailed _) <&&> _s = t+_t <&&> s@(TestFailed _) = s+TestRejected <&&> s = s+t <&&> TestRejected = t+TestSucceeded <&&> TestSucceeded = TestSucceeded -- | 'Nothing' is considered success whilst 'Just' is considered failure. fromMaybe :: Maybe Doc -> TestResult-fromMaybe Nothing = succeeded+fromMaybe Nothing = succeeded fromMaybe (Just errMsg) = failed errMsg isFailed :: TestResult -> Bool isFailed (TestFailed _) = True-isFailed _other = False+isFailed _other = False --- | A test is considered OK if it does not fail.+-- | A test is considered Ok if it does not fail. isOk :: TestResult -> Bool isOk = not . isFailed - -- 'Testable' instance is defined by converting 'TestResult' to 'QuickCheck.Property.Result'+-- | 'Testable' instance is defined by converting 'TestResult' to+-- 'QuickCheck.Property.Result' instance Q.Testable TestResult where- property TestSucceeded = Q.property Q.succeeded- property (TestFailed errorMsg) = Q.property (Q.failed{Q.reason = renderString Encode errorMsg})- property TestRejected = Q.property Q.rejected+ property TestSucceeded = Q.property Q.succeeded+ property (TestFailed errorMsg) =+ Q.property (Q.failed {Q.reason = renderString errorMsg})+ property TestRejected = Q.property Q.rejected
@@ -1,542 +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 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-
@@ -3,26 +3,16 @@ {-# OPTIONS_GHC -fno-warn-type-defaults #-} module Main ( main ) where -#ifdef DISABLE_TESTING-main :: IO ()-main = fail $ "test infrastructure not built,"- ++ " please pass --enable-tests to configure, then rebuild"-#else- import qualified Darcs.Test.Misc import qualified Darcs.Test.Patch import qualified Darcs.Test.Email-import qualified Storage.Hashed.Test+import qualified Darcs.Test.Repository.Inventory+import qualified Darcs.Test.HashedStorage import Control.Monad ( filterM ) import Control.Exception ( SomeException )-import qualified Control.Monad.Trans import Data.Text ( Text, pack, unpack )-import Data.Char ( toLower ) import Data.List ( isPrefixOf, isSuffixOf, sort )-import Data.List.Split ( splitOn )-import qualified Data.ByteString.Char8 as B-import Data.Maybe ( fromMaybe ) import GHC.IO.Encoding ( textEncodingName ) import System.Console.CmdArgs hiding ( args ) import System.Directory ( doesFileExist )@@ -40,13 +30,14 @@ -- | TODO make runnable in parallel doHashed :: IO [Test]-doHashed = return Storage.Hashed.Test.tests+doHashed = return Darcs.Test.HashedStorage.tests -- | This is the big list of tests that will be run using testrunner. unitTests :: [Test] unitTests = [ Darcs.Test.Email.testSuite , Darcs.Test.Misc.testSuite+ , Darcs.Test.Repository.Inventory.testSuite ] ++ Darcs.Test.Patch.testSuite -- ----------------------------------------------------------------------@@ -72,7 +63,7 @@ , testfile :: FilePath , testdir :: Maybe FilePath -- ^ only if you want to set it explicitly , _darcspath :: FilePath- , _diffalgorithm :: DiffAlgorithm+ , diffalgorithm :: DiffAlgorithm } deriving Typeable @@ -104,17 +95,15 @@ case code of 200 -> return Skipped _ -> Failed <$> unpack <$> lastStderr- where defaults = pack $ unlines (["ALL " ++ fmtstr, "send no-edit-description", "ALL ignore-times"] ++ dcs)+ where defaults = pack $ unlines+ [ "ALL " ++ fmtstr+ , "send no-edit-description"+ , "ALL ignore-times"+ , "ALL " ++ daf+ ] fmtstr = case fmt of Darcs2 -> "darcs-2" Darcs1 -> "darcs-1"- dcs = [dc ++ " " ++ daf | dc <- ["revert","unrevert", "whatsnew",- "record", "unpull", "obliterate",- "amend-record", "mark-conflicts",- "rebase", "pull", "repair",- "rollback", "apply",- "rebase pull", "rebase suspend",- "rebase unsuspend", "rebase obliterate"] ] daf = case da of PatienceDiff -> "patience" MyersDiff -> "myers"@@ -125,33 +114,19 @@ pathVarSeparator = ":" #endif --- TODO: add a 'all' option (implement using an Enum instance)?-readOptionList :: (String -> a) -> (String -> [a])-readOptionList readElem str = map readElem (splitOn "," str)--readDiffAlgorithm :: String -> DiffAlgorithm-readDiffAlgorithm (map toLower -> "myers") = MyersDiff-readDiffAlgorithm (map toLower -> "patience") = PatienceDiff-readDiffAlgorithm _ = error "Valid diff algorithms: myers, patience"--readRepoFormat :: String -> Format-readRepoFormat (map toLower -> "darcs-1") = Darcs1-readRepoFormat (map toLower -> "darcs-2") = Darcs2-readRepoFormat _ = error "Valid repo formats: darcs-1, darcs-2"- runtest :: ShellTest -> Sh Result runtest t = withTmp $ \dir -> do cp "tests/lib" dir cp "tests/network/sshlib" dir- cp ("tests" </> testfile t) (dir </> "test")+ cp (fromText $ pack $ testfile t) (dir </> "test") srcdir <- pwd silently $ sub $ cd dir >> runtest' t (toTextIgnore srcdir) where withTmp = case testdir t of Just dir -> \job -> do- let d = (dir </> show (format t) </> takeBaseName (testfile t))+ let d = (dir </> show (format t) </> show (diffalgorithm t) </> takeBaseName (testfile t)) mkdir_p d job d Nothing -> withTmpDir@@ -162,53 +137,22 @@ liftIO (shelly $ runtest test) shellTest :: FilePath -> Format -> Maybe FilePath -> String -> DiffAlgorithm -> Test-shellTest dp fmt tdir file da = Test (file ++ " (" ++ show fmt ++ ")" ++ " (" ++ show da ++ ")") $ ShellTest fmt file tdir dp da--hasPrefix :: B.ByteString -> B.ByteString -> Maybe B.ByteString-hasPrefix prefix =- let len = B.length prefix in- \str -> if B.take len str == prefix then Just (B.drop len str) else Nothing+shellTest dp fmt tdir file da =+ Test (takeBaseName file ++ " (" ++ show fmt ++ ")" ++ " (" ++ show da ++ ")") $+ ShellTest fmt file tdir dp da toString :: Shelly.FilePath -> String toString = unpack . toTextIgnore --- use of a pragma in a test script overrides the user's selection for that particular test,--- based on the assumption that the test author knows best-parsePragmas :: FilePath -> FilePath -> IO (FilePath, (Maybe [DiffAlgorithm], Maybe [Format]))-parsePragmas path file = do- contents <- B.lines <$> B.readFile (toString $ path </> file)- let parseLine- (_diffAlgorithms, repoFormats)- (hasPrefix (B.pack "#pragma diff-algorithm ") -> Just (readOptionList readDiffAlgorithm . B.unpack -> newDiffAlgorithms))- = (Just newDiffAlgorithms, repoFormats)- parseLine- (diffAlgorithms, _repoFormats)- (hasPrefix (B.pack "#pragma repo-format ") -> Just (readOptionList readRepoFormat . B.unpack -> newRepoFormats))- = (diffAlgorithms, Just newRepoFormats)- parseLine _ (hasPrefix (B.pack "#pragma ") -> Just pragma) = error $ "Unknown pragma " ++ B.unpack pragma ++ " in " ++ (toString $ path </> file)- parseLine x _ = x- pragmas = foldl parseLine (Nothing, Nothing) contents- return (file, pragmas)--findShell :: FilePath -> Maybe FilePath -> Bool -> [DiffAlgorithm] -> [Format] -> Sh [Test]-findShell dp tdir isFailing diffAlgorithmsDefault repoFormatsDefault =- do allFiles <- map (drop (length ("tests/"::String)) . toString) <$> ls (fromText "tests")- let files = sort $ filter relevant $ filter (".sh" `isSuffixOf`) allFiles- annotatedFiles <- Control.Monad.Trans.liftIO $ mapM (parsePragmas "tests") files+findShell :: FilePath -> Text -> Maybe FilePath -> Bool -> [DiffAlgorithm] -> [Format] -> Sh [Test]+findShell dp sdir tdir isFailing diffAlgorithms repoFormats =+ do files <- ls (fromText sdir)+ let test_files = sort $ filter relevant $ filter (hasExt "sh") files return [ shellTest dp fmt tdir file da- | (file, (diffAlgorithmsPragma, repoFormatsPragma)) <- annotatedFiles- , fmt <- fromMaybe repoFormatsDefault repoFormatsPragma- , da <- fromMaybe diffAlgorithmsDefault diffAlgorithmsPragma ]- where relevant = (if isFailing then id else not) . ("failing-" `isPrefixOf`)--findNetwork :: FilePath -> Maybe FilePath -> [DiffAlgorithm] -> [Format] -> Sh [Test]-findNetwork dp tdir diffAlgorithmsDefault repoFormatsDefault =- do files <- sort <$> filter (".sh" `isSuffixOf`) <$> map (drop (length ("tests/network/"::String)) . toString) <$> ls "tests/network"- annotatedFiles <- Control.Monad.Trans.liftIO $ mapM (parsePragmas "tests/network") files- return [ shellTest dp fmt tdir (toString $ "network" </> file) da- | (file, (diffAlgorithmsPragma, repoFormatsPragma)) <- annotatedFiles- , fmt <- fromMaybe repoFormatsDefault repoFormatsPragma- , da <- fromMaybe diffAlgorithmsDefault diffAlgorithmsPragma ]+ | file <- map toString test_files+ , fmt <- repoFormats+ , da <- diffAlgorithms ]+ where relevant = (if isFailing then id else not) . ("failing-" `isPrefixOf`) . takeBaseName . toString -- ---------------------------------------------------------------------- -- harness@@ -223,6 +167,7 @@ , patience :: Bool , darcs1 :: Bool , darcs2 :: Bool+ , full :: Bool , darcs :: String , tests :: [String] , testDir :: Maybe FilePath@@ -240,12 +185,15 @@ [ 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]"+-- RELEASE BRANCH ONLY: disable network tests (too fragile)+-- , network := True += help "Run the network shell tests [yes]" , network := False += help "Run the network shell tests [no]" , unit := True += help "Run the unit tests [yes]" , myers := False += help "Use myers diff [no]" , patience := True += help "Use patience diff [yes]" += name "p" , darcs1 := False += help "Use darcs-1 repo format [no]" += name "1" , darcs2 := True += help "Use darcs-2 repo format [yes]" += name "2"+ , full := False += help "Run all tests in all variants" , darcs := "" += help "Darcs binary path" += typ "PATH" , tests := [] += help "Pattern to limit the tests to run" += typ "PATTERN" += name "t" , testDir := Nothing += help "Directory to run tests in" += typ "PATH" += name "d"@@ -306,12 +254,14 @@ . (if patience conf then (PatienceDiff:) else id) $ [] - ftests <- shelly $ if failing conf then findShell darcsBin (testDir conf) True diffAlgorithm repoFormat else return []- stests <- shelly $ if shell conf then findShell darcsBin (testDir conf) False diffAlgorithm repoFormat else return []+ stests <- shelly $+ if shell conf+ then findShell darcsBin "tests" (testDir conf) (failing conf) 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 []+ ntests <- shelly $ if network conf then findShell darcsBin "tests/network" (testDir conf) (failing conf) diffAlgorithm repoFormat else return [] hstests <- if hashed conf then doHashed else return []- defaultMainWithArgs (ftests ++ stests ++ utests ++ ntests ++ hstests) args+ defaultMainWithArgs (stests ++ utests ++ ntests ++ hstests) args where exeSuffix :: String #ifdef WIN32@@ -326,6 +276,15 @@ hSetBinaryMode stderr True hSetBinaryMode stdin True clp <- cmdArgs_ defaultConfig- run clp--#endif+ run $+ if full clp then clp+ { hashed = True+ , shell = True+ , network = True+ , unit = True+ , myers = True+ , patience = True+ , darcs1 = True+ , darcs2 = True+ }+ else clp
@@ -1,1 +1,1 @@-Just "\nContext:\n\n[TAG 2.12.5\nGanesh Sittampalam <ganesh@earth.li>**20170111174540\n Ignore-this: 5e9ed0bfd6de3ef43d6933f2bda92147\n] \n"+Just "\nContext:\n\n[branch-2.14: disable network tests\nGuillaume Hoffmann <guillaumh@gmail.com>**20180404141317\n Ignore-this: 8d6e4b0590002fbe7b0a1d1299d4b\n] \n\n[branch-2.14: fix cabal sdist on newer Cabals\nGuillaume Hoffmann <guillaumh@gmail.com>**20180404141206\n Ignore-this: 69de5e3a3c40f4fc94536db0e7f276a9\n The trick we found is to remove other-modules: Version from\n the darcs executable.\n] \n\n[TAG 2.14.0\nGuillaume Hoffmann <guillaumh@gmail.com>**20180404141149\n Ignore-this: 6181e294bb45bb98b7c9e481990534c0\n] \n"
@@ -1,1 +1,1 @@-Just 0+Just 2
@@ -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 #-}
@@ -18,13 +18,11 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Patch- ( RepoPatch- , RepoType+ ( RepoType , IsRepoType , PrimOf , Named , WrappedNamed- , Patchy , fromPrim , fromPrims , rmfile@@ -39,7 +37,9 @@ , binary , description , showContextPatch+ , ShowPatchFor(..) , showPatch+ , displayPatch , showNicely , infopatch , changepref@@ -88,15 +88,18 @@ , getdeps , listConflictedFiles , isInconsistent+ , module Darcs.Patch.RepoPatch ) where -import Darcs.Patch.Apply ( applyToFilePaths, effectOnFilePaths, applyToTree,- maybeApplyToTree, ApplyState )-import Darcs.Patch.Commute ( commuteFL, commuteFLorComplain, commuteRL )+import Darcs.Patch.Apply ( apply,applyToFilePaths, effectOnFilePaths, applyToTree,+ maybeApplyToTree )+import Darcs.Patch.Commute ( commute, commuteFL, commuteFLorComplain, commuteRL ) import Darcs.Patch.Conflict ( listConflictedFiles, resolveConflicts ) import Darcs.Patch.Effect ( Effect(effect) )-import Darcs.Patch.Invert ( invertRL, invertFL )+import Darcs.Patch.Invert ( invert, invertRL, invertFL )+import Darcs.Patch.Inspect ( listTouchedFiles, hunkMatches )+import Darcs.Patch.Merge ( merge ) import Darcs.Patch.Named ( Named, adddeps, namepatch, anonymous,@@ -104,14 +107,6 @@ infopatch, patch2patchinfo, patchname, patchcontents ) import Darcs.Patch.Named.Wrapped ( WrappedNamed )-import Darcs.Patch.Patchy ( Patchy,- showPatch, showNicely, showContextPatch,- invert,- thing, things,- apply,- description, summary, summaryFL,- commute, listTouchedFiles, hunkMatches- ) import Darcs.Patch.Prim ( fromPrims, fromPrim, canonize, sortCoalesceFL,@@ -125,12 +120,7 @@ import Darcs.Patch.Repair ( isInconsistent ) import Darcs.Patch.RepoPatch ( RepoPatch ) import Darcs.Patch.RepoType ( RepoType, IsRepoType )+import Darcs.Patch.Show ( description, showPatch, showNicely, displayPatch+ , summary, summaryFL, thing, things, ShowPatchFor(..), ShowContextPatch(..) ) import Darcs.Patch.Summary ( xmlSummary, plainSummary, plainSummaryPrims ) import Darcs.Patch.TokenReplace ( forceTokReplace )-import Darcs.Patch.V1.Commute ( merge )--import Darcs.Util.Tree( Tree )---instance (Patchy p, ApplyState p ~ Tree) => Patchy (Named p)-instance (Patchy p, ApplyState p ~ Tree) => Patchy (WrappedNamed rt p)
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, OverloadedStrings, TypeSynonymInstances, MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings, TypeSynonymInstances, MultiParamTypeClasses #-} -- Copyright (C) 2010 Petr Rockai --@@ -32,16 +32,19 @@ module Darcs.Patch.Annotate (- annotate+ annotateFile , annotateDirectory , format , machineFormat , AnnotateResult+ , Annotate(..) ) where import Prelude () import Darcs.Prelude +import Control.Monad.State ( modify, modify', when, gets, State, execState )+ import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC import qualified Data.Map as M@@ -51,175 +54,171 @@ import Data.List( nub, groupBy ) import Data.Maybe( isJust, mapMaybe ) -import Control.Monad.State ( modify, when, gets, State, execState )+import qualified Darcs.Patch.Prim.FileUUID as FileUUID -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.Info ( PatchInfo(..), displayPatchInfo, piAuthor, makePatchname )+import Darcs.Patch.Named ( Named(..) )+import Darcs.Patch.Named.Wrapped ( WrappedNamed(..) )+import Darcs.Patch.PatchInfoAnd( info, PatchInfoAnd, hopefully )+import Darcs.Patch.Prim.V1.Core ( Prim(..), DirPatchType(..), FilePatchType(..) )+import Darcs.Patch.TokenReplace ( annotateReplace ) import Darcs.Patch.Witnesses.Ordered -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 )-import Darcs.Util.Diff ( getChanges )-import qualified Darcs.Util.Diff as D ( DiffAlgorithm )--#include "impossible.h"-+import Darcs.Util.Printer( renderString )+import Darcs.Util.ByteString ( linesPS, decodeLocale ) data FileOrDirectory = File | Directory deriving (Show, Eq) +type AnnotateResult = V.Vector (Maybe PatchInfo, B.ByteString)+ data Annotated = Annotated- { annotated :: V.Vector (Maybe PatchInfo, B.ByteString)- , current :: [(Int, B.ByteString)]- , path :: Maybe FileName+ { annotated :: !AnnotateResult+ , current :: ![(Int, B.ByteString)]+ , path :: (Maybe FileName) , what :: FileOrDirectory , currentInfo :: PatchInfo- , diffAlgorithm :: D.DiffAlgorithm } deriving Show -type AnnotateResult = V.Vector (Maybe PatchInfo, B.ByteString)- type AnnotatedM = State Annotated -instance ApplyMonad Tree AnnotatedM where- type ApplyMonadBase AnnotatedM = AnnotatedM-- nestedApply _ _ = undefinedFun "nestedApply"- liftApply _ _ = undefinedFun "liftApply"- getApplyState = undefinedFun "getApplyState"--instance ApplyMonadTree AnnotatedM where- mReadFilePS = undefinedFun "mReadFilePS"-- mDoesFileExist _ = return True- mDoesDirectoryExist _ = return True- mCreateDirectory _ = return ()- mCreateFile _ = return ()-- mRemoveFile f = do- p <- gets path- when (p == Just f) $ modify (\x -> x { path = Nothing })- updateDirectory f+class Annotate p where+ annotate :: p wX wY -> AnnotatedM () - mRemoveDirectory = mRemoveFile+instance Annotate Prim where+ annotate (FP fn fp) = case fp of+ RmFile -> do+ whenPathIs fn $ modify' (\s -> s { path = Nothing })+ whenWhatIs Directory $ updateDirectory fn+ AddFile -> return ()+ Hunk off o n -> whenPathIs fn $ whenWhatIs File $ do+ let remove = length o+ let add = length n+ i <- gets currentInfo+ c <- gets current+ a <- gets annotated+ -- NOTE patches are inverted and in inverse order+ modify' $ \s ->+ -- NOTE subtract one from offset because darcs counts from one,+ -- whereas vectors and lists count from zero.+ let (to,from) = splitAt (off-1) c+ in s { current = map eval $ to ++ replicate add (-1, B.empty) ++ drop remove from+ , annotated = merge i a $ map eval $ take remove $ from+ }+ TokReplace t o n -> whenPathIs fn $ whenWhatIs File $ do+ let test = annotateReplace t (BC.pack o) (BC.pack n)+ i <- gets currentInfo+ c <- gets current+ a <- gets annotated+ modify' $ \s -> s+ { current = map (\(ix,b)->if test b then (-1,B.empty) else (ix,b)) c+ , annotated = merge i a $ map eval $ filter (test . snd) $ c+ }+ -- TODO what if the status of a file changed from text to binary?+ Binary _ _ -> whenPathIs fn $ bug "annotate: can't handle binary changes"+ annotate (DP _ AddDir) = return ()+ annotate (DP fn RmDir) = whenWhatIs Directory $ do+ whenPathIs fn $ modify' (\s -> s { path = Nothing })+ updateDirectory fn+ annotate (Move fn fn') = do+ modify' (\s -> s { path = fmap (movedirfilename fn fn') (path s) })+ whenWhatIs Directory $ do+ let fix (i, x) = (i, fn2ps $ movedirfilename fn fn' (ps2fn x))+ modify $ \s -> s { current = map fix $ current s }+ annotate (ChangePref _ _ _) = return () - mRename a b = do- p <- gets path- w <- gets what- when (isJust p) $- modify $ \st -> st { path = Just $ movedirfilename a b (fromJust p) }- when (w == Directory) $ do- let fix (i, x) = (i, fn2ps $ movedirfilename a b (ps2fn x))- modify $ \st -> st { current = map fix $ current st }+instance Annotate FileUUID.Prim where+ annotate _ = bug "annotate not implemented for FileUUID patches" - mModifyFilePS f job = do- p <- gets path- when (p == Just f) $ updateFile (fmap linesPS . job . unlinesPS)+instance Annotate p => Annotate (FL p) where+ annotate = sequence_ . mapFL annotate - mModifyFilePSs f job = do- p <- gets path- when (p == Just f) $ updateFile job+instance Annotate p => Annotate (Named p) where+ annotate (NamedP _ _ p) = annotate p -undefinedFun :: Monad m- => String- -> m a-undefinedFun name = fail $ name ++ " undefined for Annotated"+instance Annotate p => Annotate (WrappedNamed rt p) where+ annotate (NormalP n) = annotate n+ annotate (RebaseP _ _) = bug "annotate not implemented for Rebase patches" +instance Annotate p => Annotate (PatchInfoAnd rt p) where+ annotate = annotate . hopefully -updateFile :: ([B.ByteString]- -> AnnotatedM [B.ByteString])- -> AnnotatedM ()-updateFile job = (==File) <$> gets what >>= flip when go- where- go = do- before <- map snd `fmap` gets current- after <- job before- da <- gets diffAlgorithm- reannotate $ getChanges da before after+whenWhatIs :: FileOrDirectory -> AnnotatedM () -> AnnotatedM ()+whenWhatIs w actions = do+ w' <- gets what+ when (w == w') actions - reannotate [] = return ()- reannotate ((off, remove, add):rest) = do- i <- gets currentInfo- c <- gets current- a <- gets annotated- modify $ \s -> s { current = take off c ++ [ (-1, x) | x <- add ] ++- drop (off + length remove) c- , annotated = merge i a $ take (length remove) $ drop off c- }- reannotate rest+whenPathIs :: FileName -> AnnotatedM () -> AnnotatedM ()+whenPathIs fn actions = do+ p <- gets path+ when (p == Just fn) actions - merge i a l = a V.// [ (line, (Just i, B.empty))- | (line, _) <- l, line >= 0 && line < V.length a]+eval :: (Int, B.ByteString) -> (Int, B.ByteString)+eval (i,b) = seq i $ seq b $ (i,b) +merge :: a+ -> V.Vector (Maybe a, BC.ByteString)+ -> [(Int, t)]+ -> V.Vector (Maybe a, BC.ByteString)+merge i a l = a V.// [ (line, (Just i, B.empty))+ | (line, _) <- l, line >= 0 && line < V.length a] updateDirectory :: FileName -> AnnotatedM ()-updateDirectory p = (==Directory) <$> gets what >>= flip when go+updateDirectory p = whenWhatIs Directory $ do+ let line = fn2ps p+ files <- gets current+ case filter ((==line) . snd) files of+ [match@(ident, _)] -> reannotate ident match line+ _ -> return () where- go = do let line = fn2ps p- files <- gets current- case filter ((==line) . snd) files of- [match@(ident, _)] -> reannotate ident match line- _ -> return () reannotate ident match line = modify $ \x -> x { annotated = annotated x V.// [ (ident, update line $ currentInfo x) ] , current = filter (/= match) $ current x } update line inf = (Just inf, BC.concat [ " -- created as: ", line ]) - complete :: Annotated -> Bool complete x = V.all (isJust . fst) $ annotated x --annotate' :: (Apply p, ApplyState p ~ Tree)+annotate' :: Annotate p => FL (PatchInfoAnd rt p) wX wY -> Annotated -> Annotated annotate' NilFL ann = ann annotate' (p :>: ps) ann | complete ann = ann- | otherwise = annotate' ps $ execState (apply p) (ann { currentInfo = info p })-+ | otherwise = annotate' ps $ execState (annotate p) (ann { currentInfo = info p }) -annotate :: (Apply p, ApplyState p ~ Tree)- => D.DiffAlgorithm- -> FL (PatchInfoAnd rt p) wX wY- -> FileName- -> B.ByteString- -> AnnotateResult-annotate da patches inipath inicontent = annotated $ annotate' patches initial+annotateFile :: Annotate p+ => FL (PatchInfoAnd rt p) wX wY+ -> FileName+ -> B.ByteString+ -> AnnotateResult+annotateFile patches inipath inicontent = annotated $ annotate' patches initial where initial = Annotated { path = Just inipath- , currentInfo = error "There is no currentInfo."+ , currentInfo = bug "There is no currentInfo." , current = zip [0..] (linesPS inicontent) , what = File , annotated = V.replicate (length $ breakLines inicontent) (Nothing, B.empty)- , diffAlgorithm = da } --annotateDirectory :: (Apply p, ApplyState p ~ Tree)- => D.DiffAlgorithm- -> FL (PatchInfoAnd rt p) wX wY+annotateDirectory :: Annotate p+ => FL (PatchInfoAnd rt p) wX wY -> FileName -> [FileName] -> AnnotateResult-annotateDirectory da patches inipath inicontent = annotated $ annotate' patches initial+annotateDirectory patches inipath inicontent = annotated $ annotate' patches initial where initial = Annotated { path = Just inipath- , currentInfo = error "There is no currentInfo."+ , currentInfo = bug "There is no currentInfo." , current = zip [0..] (map fn2ps inicontent) , what = Directory , annotated = V.replicate (length inicontent) (Nothing, B.empty)- , diffAlgorithm = da } - machineFormat :: B.ByteString -> AnnotateResult -> String machineFormat d a = unlines [ case i of Just inf -> show $ makePatchname inf@@ -228,10 +227,7 @@ ++ " | " ++ BC.unpack line ++ " " ++ BC.unpack add | ((i, add), line) <- zip (V.toList a) (breakLines d) ] --format :: B.ByteString- -> AnnotateResult- -> String+format :: B.ByteString -> AnnotateResult -> String format d a = pi_list ++ "\n" ++ numbered where numberedLines = zip [(1 :: Int)..] . lines $ file@@ -244,7 +240,7 @@ numbered = unlines . map prependNum $ numberedLines - pi_list = unlines [ show n ++ ": " ++ renderString Encode (showPatchInfoUI i)+ pi_list = unlines [ show n ++ ": " ++ renderString (displayPatchInfo i) | (n :: Int, i) <- zip [1..] pis ] file = concat [ annotation (fst $ head chunk) ++ " | " ++ line (head chunk) ++@@ -257,7 +253,7 @@ file_ann = groupBy ((==) `on` fst) $ zip (V.toList a) (breakLines d) - line ((_, add), l) = BC.unpack $ BC.concat [l, " ", add]+ line ((_, add), l) = decodeLocale $ BC.concat [l, " ", add] annotation (Just i, _) | Just n <- M.lookup i pi_map = pad 20 (piMail i) ++ " " ++ pad 4 ('#' : show n)@@ -271,11 +267,8 @@ | '<' `elem` piAuthor pi = takeWhile (/= '>') . drop 1 . dropWhile (/= '<') $ piAuthor pi | otherwise = piAuthor pi --breakLines :: BC.ByteString- -> [BC.ByteString]+breakLines :: BC.ByteString -> [BC.ByteString] breakLines s = case BC.split '\n' s of [] -> [] split | BC.null (last split) -> init split | otherwise -> split-
@@ -16,7 +16,7 @@ -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, MultiParamTypeClasses #-}+{-# LANGUAGE MultiParamTypeClasses #-} -- |@@ -34,25 +34,20 @@ , applyToTree , applyToState , maybeApplyToTree- , applyToFileMods , effectOnFilePaths ) where import Prelude () import Darcs.Prelude -import Data.Set ( Set )- import Control.Exception ( catch, IOException ) import Control.Arrow ( (***) ) import Darcs.Util.Tree( Tree ) import Darcs.Patch.ApplyMonad ( ApplyMonad(..), withFileNames, ApplyMonadTrans(..) )-import Darcs.Util.Path( FileName, fn2fp, fp2fn )+import Darcs.Util.Path( fn2fp, fp2fn ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..) )-import Darcs.Patch.Index.Monad ( withPatchMods )-import Darcs.Patch.Index.Types ( PatchMod ) class Apply p where type ApplyState p :: (* -> *) -> *@@ -90,7 +85,7 @@ -- | Apply a patch to a 'Tree', yielding a new 'Tree'.-applyToTree :: (Apply p, Functor m, Monad m, ApplyState p ~ Tree)+applyToTree :: (Apply p, Monad m, ApplyState p ~ Tree) => p wX wY -> Tree m -> m (Tree m)@@ -109,8 +104,3 @@ -> 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-applyToFileMods :: (Apply p, ApplyState p ~ Tree) => p wX wY -> Set FileName -> (Set FileName, [PatchMod FileName])-applyToFileMods patch = withPatchMods (apply patch)
@@ -1,9 +1,7 @@ {-# OPTIONS_GHC -fno-warn-missing-methods -fno-warn-orphans #-} {-# LANGUAGE TypeSynonymInstances, MultiParamTypeClasses- , ConstraintKinds, UndecidableInstances, CPP #-}-#if __GLASGOW_HASKELL__ >= 800-{-# LANGUAGE UndecidableSuperClasses #-}-#endif+ , ConstraintKinds, UndecidableInstances+ , UndecidableSuperClasses #-} -- Copyright (C) 2010, 2011 Petr Rockai -- -- Permission is hereby granted, free of charge, to any person@@ -40,9 +38,8 @@ import qualified Darcs.Util.Tree.Monad as TM import Darcs.Util.Tree ( Tree ) import Data.Maybe ( fromMaybe )-import Darcs.Util.ByteString( linesPS, unlinesPS )-import Darcs.Util.Path ( FileName, movedirfilename, fn2fp, isParentOrEqOf,- floatPath, AnchoredPath )+import Darcs.Util.Path+ ( FileName, movedirfilename, fn2fp, isParentOrEqOf, floatPath, AnchoredPath ) import Control.Monad.State.Strict import Control.Monad.Identity( Identity ) import Darcs.Patch.MonadProgress@@ -75,8 +72,6 @@ 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 ()@@ -84,8 +79,6 @@ 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 ()
@@ -2,22 +2,21 @@ ( applyPatches ) where -import Darcs.Patch.Info ( showPatchInfoUI )+import Darcs.Patch.Info ( displayPatchInfo ) import Darcs.Patch.ApplyMonad ( ApplyMonad(..) ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, hopefully, info ) import Darcs.Patch.Apply ( Apply(..) )-import Darcs.Patch.Patchy ( Patchy ) import Darcs.Patch.MonadProgress ( MonadProgress, ProgressAction(..), runProgressActions) import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL ) import Darcs.Util.Printer ( text, ($$) ) -applyPatches :: (MonadProgress m, ApplyMonad (ApplyState p) m, Patchy p)+applyPatches :: (MonadProgress m, ApplyMonad (ApplyState p) m, Apply p) => FL (PatchInfoAnd rt p) wX wY -> m () applyPatches ps = runProgressActions "Applying patch" (mapFL doApply ps) where doApply hp = ProgressAction { paAction = apply (hopefully hp)- , paMessage = showPatchInfoUI (info hp)+ , paMessage = displayPatchInfo (info hp) , paOnError = text "Unapplicable patch:" $$- showPatchInfoUI (info hp)+ displayPatchInfo (info hp) }
@@ -32,10 +32,10 @@ fromPrim p = Singleton (fromPrim p) instance ShowPatchBasic p => ShowPatchBasic (Bracketed p) where- showPatch (Singleton p) = showPatch p- showPatch (Braced NilFL) = blueText "{" $$ blueText "}"- showPatch (Braced ps) = blueText "{" $$ vcat (mapFL showPatch ps) $$ blueText "}"- showPatch (Parens ps) = blueText "(" $$ vcat (mapFL showPatch ps) $$ blueText ")"+ showPatch f (Singleton p) = showPatch f p+ showPatch _ (Braced NilFL) = blueText "{" $$ blueText "}"+ showPatch f (Braced ps) = blueText "{" $$ vcat (mapFL (showPatch f) ps) $$ blueText "}"+ showPatch f (Parens ps) = blueText "(" $$ vcat (mapFL (showPatch f) ps) $$ blueText ")" -- the ReadPatch instance is defined in Darcs.Patch.Read as it is -- used as an intermediate form during reading of lists of patches
@@ -15,19 +15,13 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.Patch.Bundle- ( hashBundle- , makeBundle2- , makeBundleN+ ( makeBundleN , scanBundle , contextPatches , scanContextFile , patchFilename- , getContext , minContext- , parseBundle ) where import Prelude ()@@ -50,7 +44,7 @@ import Darcs.Patch.Depends ( slightlyOptimizePatchset ) import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Info ( PatchInfo, readPatchInfo, showPatchInfo,- showPatchInfoUI, isTag )+ displayPatchInfo, isTag ) import Darcs.Patch.Named.Wrapped ( WrappedNamed ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, piap, fmapFLPIAP, info, patchInfoAndPatch, unavailable, hopefully,@@ -59,17 +53,18 @@ 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.Show ( ShowPatchBasic, ShowPatchFor(ForStorage) ) import Darcs.Patch.Witnesses.Ordered ( RL(..), FL(..), (:>)(..), reverseFL, (+<+), mapFL, mapFL_FL, mapRL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), seal ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) -import Darcs.Util.ByteString ( mmapFilePS, linesPS, unlinesPS, dropSpace, substrPS)-import Darcs.Util.Crypt.SHA1 ( sha1PS )+import Darcs.Util.ByteString+ ( mmapFilePS, linesPS, unlinesPS, dropSpace, substrPS, decodeLocale )+import Darcs.Util.Hash ( sha1PS ) import Darcs.Util.Printer ( Doc, renderPS, newline, text, ($$),- (<>), vcat, vsep, renderString, RenderMode(..) )+ (<>), vcat, vsep, renderString ) -- |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@@ -77,7 +72,7 @@ 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+ show $ sha1PS $ renderPS $ vcat (mapFL (showPatch ForStorage) to_be_sent) <> newline makeBundleN :: (ApplyState p ~ Tree, RepoPatch p) => Maybe (Tree IO) -> PatchSet rt p wStart wX -> FL (WrappedNamed rt p) wX wY -> IO Doc@@ -96,8 +91,8 @@ -> 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- Nothing -> return (vsep $ mapFL showPatch to_be_sent)+ Just tree -> fst `fmap` virtualTreeIO (showContextPatch ForStorage to_be_sent) tree+ Nothing -> return (vsep $ mapFL (showPatch ForStorage) to_be_sent) return $ format patches where format the_new = text ""@@ -107,7 +102,7 @@ $$ text "" $$ text "Context:" $$ text ""- $$ vcat (map showPatchInfo common)+ $$ vcat (map (showPatchInfo ForStorage) common) $$ text "Patch bundle hash:" $$ text (hashBundle to_be_sent2) $$ text ""@@ -153,7 +148,7 @@ sealCtxAndPs ctx ps = Right $ sealContextWithPatches ctx ps - sealContextWithPatches :: RepoPatch p => [PatchInfo]+ sealContextWithPatches :: [PatchInfo] -> FL (PatchInfoAnd ('RepoType 'NoRebase) (Bracketed p)) wX wY -> Sealed ((PatchSet rt p :> FL (PatchInfoAnd rt p)) Origin)@@ -201,14 +196,14 @@ -- |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 rt p) wX wY+unavailablePatches :: [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 rt p wX wY+piUnavailable :: PatchInfo -> PatchInfoAnd rt p wX wY piUnavailable i = patchInfoAndPatch i . unavailable $- "Patch not stored in patch bundle:\n" ++ renderString Encode (showPatchInfoUI i)+ "Patch not stored in patch bundle:\n" ++ renderString (displayPatchInfo i) -- |getContext parses a context list, returning a tuple containing the list, -- and remaining ByteString input.@@ -238,11 +233,11 @@ -- removed any leading spaces. The before-newline part is unpacked to a String, -- and tupled up with the remaining ByteString. sillyLex :: B.ByteString -> (String, B.ByteString)-sillyLex ps = (BC.unpack a, b)+sillyLex ps = (decodeLocale a, b) where (a, b) = BC.break (== '\n') (dropSpace ps) -contextPatches :: RepoPatch p => PatchSet rt p Origin wX+contextPatches :: PatchSet rt p Origin wX -> (PatchSet rt p :> RL (PatchInfoAnd rt p)) Origin wX contextPatches set = case slightlyOptimizePatchset set of PatchSet (ts :<: Tagged t _ ps') ps ->@@ -250,11 +245,11 @@ PatchSet NilRL ps -> PatchSet NilRL NilRL :> ps -- |'scanContextFile' scans the context in the file of the given name.-scanContextFile :: RepoPatch p => FilePath -> IO (PatchSet rt p Origin wX)+scanContextFile :: 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 rt p Origin wX+ scanContext :: B.ByteString -> PatchSet rt p Origin wX scanContext input | B.null input = error "Bad context!" | otherwise = case sillyLex input of
@@ -15,116 +15,134 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}----- | PatchChoices divides a sequence of patches into three sets: "first",--- "middle" and "last", such that all patches can be applied, if you first--- apply the first ones then the middle ones and then the last ones.--- Obviously if there are dependencies between the patches that will put a--- constraint on how you can choose to divide them up. The PatchChoices data--- type and associated functions are here to deal with many of the common+-- | The purpose of this module is to deal with many of the common -- cases that come up when choosing a subset of a group of patches. ----- 'forceLast' tells PatchChoices that a particular patch is required to be in--- the "last" group, which also means that any patches that depend on it--- must be in the "last" group.+-- The idea is to divide a sequence of candidate patches into an initial+-- section named 'InFirst', a final section named 'InLast', and between them a+-- third section of not yet decided patches named 'InMiddle'. The reason for the+-- neutral terminology 'InFirst', 'InMiddle', and 'InLast', is that which of 'InFirst'+-- and 'InLast' counts as @selected@ or @deselected@ depends on+-- what we want to achive, that is, on the command and its options.+-- See "Darcs.UI.SelectChanges" for examples of how to use the functions from+-- this module. ----- Internally, a PatchChoices doesn't always reorder the patches until--- it is asked for the final output (e.g. by 'get_first_choice').--- Instead, each patch is placed in a state of definitely first,--- definitely last and undecided; undecided leans towards--- "middle". The patches that are first are commuted to the head--- immediately, but patches that are middle and last are mixed--- together. In case you're wondering about the first-middle-last--- language, it's because in some cases the "yes" answers will be last--- (as is the case for the revert command), and in others first (as in--- record, pull and push).+-- Obviously if there are dependencies between the patches that will put a+-- constraint on how you can choose to divide them up. Unless stated otherwise,+-- functions that move patches from one section to another pull all dependent+-- patches with them. ----- Some patch marked "middle" may in fact be unselectable because of--- dependencies: when a patch is marked "last", its dependencies are--- not updated until patchSlot is called on them.-module Darcs.Patch.Choices ( PatchChoices, patchChoices, patchChoicesLps,- patchChoicesLpsSub,- patchSlot, patchSlot',- getChoices, refineChoices,- separateFirstMiddleFromLast,- separateFirstFromMiddleLast,- forceFirst, forceFirsts, forceLast, forceLasts,- forceMatchingFirst, forceMatchingLast,- selectAllMiddles,- makeUncertain, makeEverythingLater, makeEverythingSooner,- LabelledPatch, Label, label, lpPatch, getLabelInt,- Slot(..),- substitute- ) where+-- Internally, we don't necessarily reorder patches immediately, but merely+-- tag them with the desired status, and thus postpone the actual commutation.+-- This saves a lot of unnecessary work, especially when choices are made+-- interactively, where the user can revise earlier decisions.+module Darcs.Patch.Choices+ ( -- * Choosing patches+ PatchChoices+ , Slot(..)+ -- ** Constructing+ , patchChoices+ , mkPatchChoices+ -- ** Querying+ , patchSlot+ , getChoices+ , separateFirstMiddleFromLast+ , separateFirstFromMiddleLast+ -- ** Forcing patches into a given 'Slot'+ , forceMatchingFirst+ , forceFirsts+ , forceFirst+ , forceMatchingLast+ , forceLasts+ , forceLast+ , forceMiddle+ , makeEverythingSooner+ , makeEverythingLater+ -- ** Operations on 'InMiddle' patches+ , selectAllMiddles+ , refineChoices+ -- ** Substitution+ , substitute+ -- * Labelling patches+ , LabelledPatch+ , Label+ , label+ , unLabel+ , labelPatches+ , getLabelInt+ ) where import Prelude () import Darcs.Prelude -import Control.Monad.Identity ( Identity )-import Control.Monad.State ( StateT(..) )--import Prelude hiding ( pred )--import Darcs.Patch- ( Patchy, commuteRL, commute, merge, listTouchedFiles, hunkMatches- , invert )-import Darcs.Patch.Merge ( Merge )+import Darcs.Patch.Merge ( Merge, merge )+import Darcs.Patch.Invert ( Invert, invert )+import Darcs.Patch.Commute ( Commute, commute, commuteRL )+import Darcs.Patch.Inspect ( PatchInspect, listTouchedFiles, hunkMatches ) import Darcs.Patch.Permutations ( commuteWhatWeCanRL, commuteWhatWeCanFL )-import Darcs.Patch.Patchy ( Invert, Commute, PatchInspect )-import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) )-import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )+import Darcs.Patch.Witnesses.Eq ( Eq2(..), EqCheck(..) ) import Darcs.Patch.Witnesses.Ordered- ( FL(..), RL(..),- (:>)(..), (:\/:)(..), (:/\:)(..), (:||:)(..),- zipWithFL, mapFL_FL, concatFL,- (+>+), reverseRL, anyFL )+ ( FL(..), RL(..)+ , (:>)(..), (:\/:)(..), (:/\:)(..), (:||:)(..)+ , zipWithFL, mapFL_FL, concatFL+ , (+>+), reverseRL, anyFL ) import Darcs.Patch.Witnesses.Sealed ( Sealed2(..) )---#include "impossible.h"-+import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) -- | 'Label' @mp i@ acts as a temporary identifier to help us keep track of patches -- during the selection process. These are useful for finding patches that -- may have moved around during patch selection (being pushed forwards or -- backwards as dependencies arise). ----- The identifier is implemented as a tuple @Label mp i@. The @i@ is just some--- arbitrary label, expected to be unique within the patches being+-- The identifier is implemented as a tuple @Label mp i@. The @i@ is an+-- integer, expected to be unique within the patches being -- scrutinised. The @mp@ is motivated by patch splitting; it -- provides a convenient way to generate a new identifier from the patch -- being split. For example, if we split a patch identified as @Label Nothing--- 5@, the resulting sub-patches could be identified as @Label (Label Nothing 5)--- 1@, @Label (Label Nothing 5) 2@, etc.-data Label = Label (Maybe Label) Integer deriving ( Eq, Ord )+-- 5@, the resulting sub-patches could be identified as+-- @Label (Just (Label Nothing 5))1@, @Label (Just (Label Nothing 5)) 2@, etc.+--+-- IOW, 'Label' is a non-empty, reversed list of 'Int's.+data Label = Label (Maybe Label) Int deriving Eq++-- | A patch with a 'Label' attached to it. data LabelledPatch p wX wY = LP Label (p wX wY) --- | The @Bool@ parameter indicates whether the patch has been explicitely--- selected (or rejected) by the user.-data PatchChoice p wX wY = PC { pcPatch :: (LabelledPatch p wX wY)- , _pcChoice :: Bool}+-- | This internal type tags a 'LabelledPatch' with a 'Bool', to distinguish+-- 'InMiddle' from 'InLast' patches.+data PatchChoice p wX wY = PC+ { pcPatch :: (LabelledPatch p wX wY) -- ^ the 'LabelledPatch' in question+ , _pcIsLast :: Bool -- ^ 'False' = 'InMiddle', 'True' = 'InLast'+ } +-- | Internal function to tag a 'LabelledPatch' as 'InMiddle' or 'InLast'.+pcSetLast :: Bool -> LabelledPatch p wX wY -> PatchChoice p wX wY+pcSetLast = flip PC++-- TODO pcsFirsts should be an 'RL', not an 'FL'.+-- | A sequence of 'LabelledPatch'es where each patch is either+-- 'InFirst', 'InMiddle', or 'InLast'. The representation is+-- optimized for the case where we start chosing patches from the left+-- of the sequence: patches that are 'InFirst' are commuted to the head+-- immediately, but patches that are 'InMiddle' or 'InLast' are mixed+-- together; when a patch is marked 'InLast', its dependencies are+-- not updated until we retrieve the final result. data PatchChoices p wX wY where PCs :: { pcsFirsts :: FL (LabelledPatch p) wX wM- , pcsLasts :: FL (PatchChoice p) wM wY}+ , pcsMiddleLasts :: FL (PatchChoice p) wM wY} -> PatchChoices p wX wY --- | See module documentation for 'Darcs.Patch.Choices'+-- | See module documentation for "Darcs.Patch.Choices". data Slot = InFirst | InMiddle | InLast label :: LabelledPatch p wX wY -> Label label (LP tg _) = tg -getLabelInt :: Label -> Integer+getLabelInt :: Label -> Int getLabelInt (Label _ i) = i -lpPatch :: LabelledPatch p wX wY -> p wX wY-lpPatch (LP _ p) = p--liftLP :: (p wX wY -> p wA wB) -> (LabelledPatch p wX wY -> LabelledPatch p wA wB)-liftLP f (LP t p) = LP t (f p)+unLabel :: LabelledPatch p wX wY -> p wX wY+unLabel (LP _ p) = p -- This is dangerous if two patches from different labelled series are compared -- ideally Label (and hence LabelledPatch/PatchChoices) would have a witness type@@ -132,75 +150,94 @@ compareLabels :: LabelledPatch p wA wB -> LabelledPatch p wC wD -> EqCheck (wA, wB) (wC, wD) compareLabels (LP l1 _) (LP l2 _) = if l1 == l2 then unsafeCoerceP IsEq else NotEq -instance MyEq p => MyEq (LabelledPatch p) where- unsafeCompare (LP l1 p1) (LP l2 p2) = l1 == l2 && unsafeCompare p1 p2+instance Eq2 p => Eq2 (LabelledPatch p) where+ unsafeCompare (LP l1 p1) (LP l2 p2) = l1 == l2 && unsafeCompare p1 p2 instance Invert p => Invert (LabelledPatch p) where- invert = liftLP invert+ invert (LP t p) = LP t (invert p) instance Commute p => Commute (LabelledPatch p) where- commute (LP l1 p1 :> LP l2 p2) = do p2' :> p1' <- commute (p1 :> p2)- return (LP l2 p2' :> LP l1 p1')+ commute (LP l1 p1 :> LP l2 p2) = do+ p2' :> p1' <- commute (p1 :> p2)+ return (LP l2 p2' :> LP l1 p1') instance PatchInspect p => PatchInspect (LabelledPatch p) where- listTouchedFiles (LP _ p) = listTouchedFiles p- hunkMatches f (LP _ p) = hunkMatches f p+ listTouchedFiles = listTouchedFiles . unLabel+ hunkMatches f = hunkMatches f . unLabel instance Merge p => Merge (LabelledPatch p) where- merge (LP l1 p1 :\/: LP l2 p2) = case merge (p1 :\/: p2) of- p2' :/\: p1' -> LP l2 p2' :/\: LP l1 p1'+ merge (LP l1 p1 :\/: LP l2 p2) =+ case merge (p1 :\/: p2) of+ p2' :/\: p1' -> LP l2 p2' :/\: LP l1 p1' instance Commute p => Commute (PatchChoice p) where- commute (PC p1 c1 :> PC p2 c2) = do p2' :> p1' <- commute (p1 :> p2)- return (PC p2' c2 :> PC p1' c1)+ commute (PC p1 c1 :> PC p2 c2) = do+ p2' :> p1' <- commute (p1 :> p2)+ return (PC p2' c2 :> PC p1' c1) instance PatchInspect p => PatchInspect (PatchChoice p) where- listTouchedFiles (PC p _) = listTouchedFiles p- hunkMatches f (PC p _) = hunkMatches f p+ listTouchedFiles = listTouchedFiles . pcPatch+ hunkMatches f = hunkMatches f . pcPatch instance Merge p => Merge (PatchChoice p) where merge (PC lp1 c1 :\/: PC lp2 c2) = case merge (lp1 :\/: lp2) of lp2' :/\: lp1' -> PC lp2' c2 :/\: PC lp1' c1 -patchChoices :: Patchy p => FL p wX wY -> PatchChoices p wX wY-patchChoices = fst . patchChoicesLps+-- | Create a 'PatchChoices' from a sequence of patches, so that+-- all patches are initially 'InMiddle'.+patchChoices :: FL p wX wY -> PatchChoices p wX wY+patchChoices = mkPatchChoices . labelPatches Nothing --- |Label a sequence of patches as subpatches of an existing label. This is intended for--- use when substituting a patch for an equivalent patch or patches.-patchChoicesLpsSub :: Patchy p- => Maybe Label -> FL p wX wY- -> (PatchChoices p wX wY, FL (LabelledPatch p) wX wY)-patchChoicesLpsSub tg ps = let lps = zipWithFL LP (map (Label tg) [1..]) ps- in (PCs NilFL (mapFL_FL (\lp -> PC lp False) lps), lps)+-- | Label a sequence of patches, maybe using the given parent label.+labelPatches :: Maybe Label -> FL p wX wY -> FL (LabelledPatch p) wX wY+labelPatches tg ps = zipWithFL LP (map (Label tg) [1..]) ps --- |Label a sequence of patches.-patchChoicesLps :: Patchy p => FL p wX wY -> (PatchChoices p wX wY, FL (LabelledPatch p) wX wY)-patchChoicesLps = patchChoicesLpsSub Nothing+-- | Create a 'PatchChoices' from an already labelled sequence of patches,+-- so that all patches are initially 'InMiddle'.+mkPatchChoices :: FL (LabelledPatch p) wX wY -> PatchChoices p wX wY+mkPatchChoices = PCs NilFL . mapFL_FL (pcSetLast False) -instance MyEq p => MyEq (PatchChoice p) where- unsafeCompare (PC lp1 _) (PC lp2 _) = unsafeCompare lp1 lp2+instance Eq2 p => Eq2 (PatchChoice p) where+ unsafeCompare (PC lp1 _) (PC lp2 _) = unsafeCompare lp1 lp2 -separateFirstFromMiddleLast :: Patchy p => PatchChoices p wX wZ- -> (FL (LabelledPatch p) :> FL (LabelledPatch p)) wX wZ-separateFirstFromMiddleLast (PCs f l) = f :> mapFL_FL (\ (PC lp _) -> lp) l+-- | Like 'getChoices' but lumps together 'InMiddle' and 'InLast' patches.+-- This is more efficient than using 'getChoices' and then catenating 'InMiddle'+-- and 'InLast' sections because we have to commute less.+-- (This is what 'PatchChoices' are optimized for.)+--+-- prop> separateFirstFromMiddleLast c == case getChoices c of f:>m:>l -> f:>m+>+l+separateFirstFromMiddleLast :: PatchChoices p wX wZ+ -> (FL (LabelledPatch p) :> FL (LabelledPatch p)) wX wZ+separateFirstFromMiddleLast (PCs f ml) = f :> mapFL_FL pcPatch ml -separateFirstMiddleFromLast :: Patchy p => PatchChoices p wX wZ- -> (FL (LabelledPatch p) :> FL (LabelledPatch p)) wX wZ+-- | Like 'getChoices' but lumps together 'InFirst' and 'InMiddle' patches.+--+-- prop> separateFirstMiddleFromLast c == case getChoices c of f:>m:>l -> f+>+m:>l+separateFirstMiddleFromLast :: Commute p+ => PatchChoices p wX wZ+ -> (FL (LabelledPatch p) :> FL (LabelledPatch p)) wX wZ separateFirstMiddleFromLast (PCs f l) = case pushLasts l of (m :> l') -> f +>+ m :> l' --- | @getChoices@ evaluates a @PatchChoices@ into the first, middle and last sequences--- by doing the commutes that were needed.-getChoices :: Patchy p => PatchChoices p wX wY- -> (FL (LabelledPatch p) :> FL (LabelledPatch p) :> FL (LabelledPatch p)) wX wY-getChoices (PCs f l) =- case pushLasts l of- (m :> l') -> f :> m :> l'+-- | Retrieve the resulting sections from a 'PatchChoice'. The result is a+-- triple @first:>middle:>last@, such that all patches in @first@ are+-- 'InFirst', all patches in @middle@ are 'InMiddle', and all patches in @last@+-- are 'InLast'.+getChoices :: Commute p+ => PatchChoices p wX wY+ -> (FL (LabelledPatch p) :> FL (LabelledPatch p) :> FL (LabelledPatch p)) wX wY+getChoices (PCs f ml) =+ case pushLasts ml of+ (m :> l') -> f :> m :> l' -pushLasts :: Patchy p => FL (PatchChoice p) wX wY- -> (FL (LabelledPatch p) :> FL (LabelledPatch p)) wX wY+-- | Internal function to commute patches in the common 'pcsMiddleLasts' segment+-- so that all 'InLast' patches are behind 'InMiddle' ones. Patches 'InMiddle'+-- that depend on any 'InLast' are promoted to 'InLast'.+pushLasts :: Commute p+ => FL (PatchChoice p) wX wY+ -> (FL (LabelledPatch p) :> FL (LabelledPatch p)) wX wY pushLasts NilFL = NilFL :> NilFL pushLasts (PC lp False :>: pcs) = case pushLasts pcs of@@ -211,27 +248,32 @@ case commuteWhatWeCanFL (lp :> m) of (m' :> lp' :> deps) -> m' :> (lp' :>: deps +>+ l) --- | @refineChoices act@ performs @act@ on the middle part of a sequence--- of choices, in order to hopefully get more patches into the @first@ and--- @last@ parts of a @PatchChoices@.-refineChoices :: (Patchy p, Monad m, Functor m) =>- (forall wU wV . FL (LabelledPatch p) wU wV ->- PatchChoices p wU wV ->- m (PatchChoices p wU wV))- -> PatchChoices p wX wY -> m (PatchChoices p wX wY)+-- TODO for the way we use this function it is too restrictive IMO: it does not+-- allow the user to select anything that doesn't match the pre-filters.+-- | Use the given monadic 'PatchChoices' transformer on the 'InMiddle' section+-- of a 'PatchChoices', then fold the result back into the original 'PatchChoices'.+refineChoices :: (Commute p, Monad m)+ => (forall wU wV . FL (LabelledPatch p) wU wV ->+ PatchChoices p wU wV -> m (PatchChoices p wU wV))+ -> PatchChoices p wX wY -> m (PatchChoices p wX wY) refineChoices act ps =- case getChoices ps of- (f :> m :> l) -> do- let mchoices = PCs NilFL . mapFL_FL (flip PC False) $ m- (PCs f' l') <- act m mchoices- return . PCs (f +>+ f') $ l' +>+ mapFL_FL (flip PC True) l+ case getChoices ps of+ (f :> m :> l) -> do+ (PCs f' l') <- act m (mkPatchChoices m)+ return . PCs (f +>+ f') $ l' +>+ mapFL_FL (pcSetLast True) l -patchSlot :: forall p wA wB wX wY. Patchy p => LabelledPatch p wA wB- -> PatchChoices p wX wY -> (Slot, PatchChoices p wX wY)-patchSlot (LP t _) pc@(PCs f l) =- if foundIn f- then (InFirst, pc)- else psLast f NilRL NilRL l+-- | Given a 'LabelledPatch' determine to which section of the given+-- 'PatchChoices' it belongs. This is not trivial to compute, since a patch+-- tagged as 'InMiddle' may be forced to actually be 'InLast' by dependencies. We+-- return a possibly re-ordered 'PatchChoices' so as not to waste the+-- commutation effort.+patchSlot :: forall p wA wB wX wY. Commute p+ => LabelledPatch p wA wB+ -> PatchChoices p wX wY+ -> (Slot, PatchChoices p wX wY)+patchSlot (LP t _) pc@(PCs f ml)+ | foundIn f = (InFirst, pc)+ | otherwise = psLast f NilRL NilRL ml where foundIn = anyFL ((== t) . label) psLast :: forall wM wC wL .@@ -243,7 +285,7 @@ psLast firsts middles bubble (PC lp True :>: ls) | label lp == t = (InLast , PCs { pcsFirsts = firsts- , pcsLasts = settleM middles+ , pcsMiddleLasts = settleM middles +>+ settleB bubble +>+ PC lp True :>: ls}) psLast firsts middles bubble (PC lp False :>: ls)@@ -251,13 +293,13 @@ case commuteRL (bubble :> lp) of Just (lp' :> bubble') -> (InMiddle, PCs { pcsFirsts = firsts- , pcsLasts = settleM middles+ , pcsMiddleLasts = settleM middles +>+ PC lp' False :>: settleB bubble' +>+ ls}) Nothing -> (InLast, PCs { pcsFirsts = firsts- , pcsLasts = settleM middles+ , pcsMiddleLasts = settleM middles +>+ settleB bubble +>+ PC lp True :>: ls})@@ -271,16 +313,14 @@ settleM middles = mapFL_FL (\lp -> PC lp False) $ reverseRL middles settleB bubble = mapFL_FL (\lp -> PC lp True) $ reverseRL bubble -patchSlot' :: Patchy p =>- LabelledPatch p wA wB -> StateT (PatchChoices p wX wY) Identity Slot-patchSlot' lp = StateT (return . patchSlot lp)--forceMatchingFirst :: forall p wA wB. Patchy p =>- ( forall wX wY . LabelledPatch p wX wY -> Bool)- -> PatchChoices p wA wB- -> PatchChoices p wA wB-forceMatchingFirst pred (PCs fn l) =- fmfLasts fn NilRL l+-- | Force all patches matching the given predicate to be 'InFirst',+-- pulling any dependencies with them. This even forces any patches+-- that were already tagged 'InLast'.+forceMatchingFirst :: forall p wA wB. Commute p+ => ( forall wX wY . LabelledPatch p wX wY -> Bool)+ -> PatchChoices p wA wB+ -> PatchChoices p wA wB+forceMatchingFirst pred (PCs f0 ml) = fmfLasts f0 NilRL ml where fmfLasts :: FL (LabelledPatch p) wA wM -> RL (PatchChoice p) wM wN@@ -295,21 +335,30 @@ in fmfLasts f' l1' l2 fmfLasts f l1 (a :>: l2) = fmfLasts f (l1 :<: a) l2 fmfLasts f l1 NilFL = PCs { pcsFirsts = f- , pcsLasts = reverseRL l1 }+ , pcsMiddleLasts = reverseRL l1 } pred_pc :: forall wX wY . PatchChoice p wX wY -> Bool pred_pc (PC lp _) = pred lp -forceFirsts :: Patchy p => [Label] -> PatchChoices p wA wB- -> PatchChoices p wA wB+-- | Force all patches labelled with one of the given labels to be 'InFirst',+-- pulling any dependencies with them. This even forces any patches+-- that were already tagged 'InLast'.+forceFirsts :: Commute p+ => [Label] -> PatchChoices p wA wB -> PatchChoices p wA wB forceFirsts ps = forceMatchingFirst ((`elem` ps) . label) -forceFirst :: Patchy p => Label -> PatchChoices p wA wB- -> PatchChoices p wA wB+-- | Force a single patch labelled with the given label to be 'InFirst',+-- pulling any dependencies with them. This even forces any patches+-- that were already tagged 'InLast'.+forceFirst :: Commute p+ => Label -> PatchChoices p wA wB -> PatchChoices p wA wB forceFirst p = forceMatchingFirst ((== p) . label) --TODO: stop after having seen the patch we want to force first -selectAllMiddles :: forall p wX wY. Patchy p => Bool- -> PatchChoices p wX wY -> PatchChoices p wX wY+-- | Make all 'InMiddle' patches either 'InFirst' or 'InLast'. This does *not*+-- modify any patches that are already determined to be 'InLast' by+-- dependencies.+selectAllMiddles :: forall p wX wY. Commute p+ => Bool -> PatchChoices p wX wY -> PatchChoices p wX wY selectAllMiddles True (PCs f l) = PCs f (mapFL_FL g l) where g (PC lp _) = PC lp True selectAllMiddles False (PCs f l) = samf f NilRL NilRL l@@ -327,73 +376,98 @@ 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)- -> PatchChoices p wA wB- -> PatchChoices p wA wB-forceMatchingLast pred (PCs f l) = do- fmlFirst pred True NilRL f l+-- | Similar to 'forceMatchingFirst' only that patches are forced to be+-- 'InLast' regardless of their previous status.+forceMatchingLast :: Commute p => (forall wX wY . LabelledPatch p wX wY -> Bool)+ -> PatchChoices p wA wB+ -> PatchChoices p wA wB+forceMatchingLast pred (PCs f ml) =+ forceMatchingMiddleOrLast pred True NilRL f ml -fmlFirst :: forall p wA wB wM1 wM2 . Patchy p =>- (forall wX wY . LabelledPatch p wX wY -> Bool) -> Bool- -> RL (LabelledPatch p) wA wM1- -> FL (LabelledPatch p) wM1 wM2- -> FL (PatchChoice p) wM2 wB- -> PatchChoices p wA wB-fmlFirst pred b f1 (a :>: f2) l- | pred a =- case commuteWhatWeCanFL (a :> f2) of- (f2' :> a' :> deps) ->- let- 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 (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) )+-- | Internal function working directly on the constituent parts of a+-- 'PatchChoices' and taking an accumulating 'RL' to build up a new 'InFirst'+-- section. It forces patches to be 'InMiddle' or 'InLast', depending+-- on the 'Bool' parameter ('True' means 'InLast', 'False' means 'InMiddle').+-- It does this regardless of the previous status of patches and also pulls+-- any dependent patches with it.+forceMatchingMiddleOrLast+ :: forall p wA wB wM1 wM2 . Commute p+ => (forall wX wY . LabelledPatch p wX wY -> Bool)+ -> Bool+ -> RL (LabelledPatch p) wA wM1 -- ^ accumulator for 'InFirst' patches+ -> FL (LabelledPatch p) wM1 wM2 -- ^ original 'InFirst' section+ -> FL (PatchChoice p) wM2 wB -- ^ original 'InMiddle' and 'InLast' section+ -> PatchChoices p wA wB+forceMatchingMiddleOrLast pred b f1 (a :>: f2) ml+ | pred a =+ case commuteWhatWeCanFL (a :> f2) of+ (f2' :> a' :> deps) ->+ let+ ml' = mapFL_FL (pcSetLast b) (a' :>: deps) +>+ ml+ in+ forceMatchingMiddleOrLast pred b f1 f2' ml'+forceMatchingMiddleOrLast pred b f1 (a :>: f2) ml =+ forceMatchingMiddleOrLast pred b (f1 :<: a) f2 ml+forceMatchingMiddleOrLast pred b f1 NilFL ml =+ PCs { pcsFirsts = reverseRL f1+ , pcsMiddleLasts = mapFL_FL choose ml+ }+ where+ choose (PC lp c) = (PC lp (if pred lp then b else c) ) -forceLasts :: Patchy p => [Label]- -> PatchChoices p wA wB -> PatchChoices p wA wB+-- | Force all patches labelled with one of the given labels to be 'InLast',+-- pulling any dependencies with them. This even forces any patches+-- that were previously tagged 'InFirst'.+forceLasts :: Commute p+ => [Label] -> PatchChoices p wA wB -> PatchChoices p wA wB forceLasts ps = forceMatchingLast ((`elem` ps) . label) -forceLast :: Patchy p => Label- -> PatchChoices p wA wB -> PatchChoices p wA wB+-- | Force a single patch labelled with the given label to be 'InLast',+-- pulling any dependencies with them, regardless of their previous status.+forceLast :: Commute p+ => Label -> PatchChoices p wA wB -> PatchChoices p wA wB forceLast p = forceMatchingLast ((== p) . label) -makeUncertain :: Patchy p => Label -> PatchChoices p wA wB -> PatchChoices p wA wB-makeUncertain t (PCs f l) = fmlFirst ((== t) . label) False NilRL f l+-- | Force a patch with the given 'Label' to be 'InMiddle',+-- pulling any dependencies with it, regardless of their previous status.+forceMiddle :: Commute p => Label -> PatchChoices p wA wB -> PatchChoices p wA wB+forceMiddle t (PCs f l) = forceMatchingMiddleOrLast ((== t) . label) False NilRL f l -makeEverythingLater :: Patchy p => PatchChoices p wX wY -> PatchChoices p wX wY-makeEverythingLater (PCs f l) =- let m = mapFL_FL (\lp -> PC lp False) f- l' = mapFL_FL (\(PC lp _) -> PC lp True) l- in- PCs NilFL $ m +>+ l'+-- | Turn 'InFirst' patches into 'InMiddle' ones and 'InMiddle' into 'InLast' ones.+makeEverythingLater :: PatchChoices p wX wY -> PatchChoices p wX wY+makeEverythingLater (PCs f ml) =+ let m = mapFL_FL (pcSetLast False) f+ ml' = mapFL_FL (\(PC lp _) -> PC lp True) ml+ in PCs NilFL $ m +>+ ml' -makeEverythingSooner :: forall p wX wY.- Patchy p => PatchChoices p wX wY -> PatchChoices p wX wY-makeEverythingSooner (PCs f l) =- case mes NilRL NilRL l- of (m :> l') ->- PCs (f +>+ m) l'+-- | Turn 'InMiddle' patches into 'InFirst' and 'InLast' patches into 'InMiddle'.+-- Does *not* pull dependencies into 'InFirst', instead patches that+-- cannot be commuted past 'InLast' patches stay 'InMiddle'.+makeEverythingSooner :: forall p wX wY. Commute p+ => PatchChoices p wX wY -> PatchChoices p wX wY+makeEverythingSooner (PCs f ml) =+ case mes NilRL NilRL ml+ of (m :> ml') ->+ PCs (f +>+ m) ml' where mes :: forall wM1 wM2 wM3 . RL (LabelledPatch p) wM1 wM2 -> 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 (bubble :<: lp) ls- mes middle bubble (PC lp False :>: ls) =+ mes middle bubble (PC lp True :>: mls) = mes middle (bubble :<: lp) mls+ mes middle bubble (PC lp False :>: mls) = case commuteRL (bubble :> lp) of- 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)+ Nothing -> mes middle (bubble :<: lp) mls+ Just (lp' :> bubble') -> mes (middle :<: lp') bubble' mls+ 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--- associated with @a@-substitute :: forall p wX wY- . Patchy p- => Sealed2 (LabelledPatch p :||: FL (LabelledPatch p))+-- | Substitute a single 'LabelledPatch' with an equivalent list of patches,+-- preserving its status as 'InFirst', 'InMiddle' or 'InLast').+-- The patch is looked up using equality of 'Label's.+substitute :: forall p wX wY .+ Sealed2 (LabelledPatch p :||: FL (LabelledPatch p)) -> PatchChoices p wX wY -> PatchChoices p wX wY substitute (Sealed2 (lp :||: new_lps)) (PCs f l) =@@ -405,5 +479,5 @@ | otherwise = lp' :>: NilFL substPc :: PatchChoice p wA wB -> FL (PatchChoice p) wA wB substPc (PC lp' c)- | IsEq <- compareLabels lp lp' = mapFL_FL (flip PC c) new_lps+ | IsEq <- compareLabels lp lp' = mapFL_FL (pcSetLast c) new_lps | otherwise = PC lp' c :>: NilFL
@@ -1,5 +1,5 @@ -- Copyright (C) 2002-2003 David Roundy, 2010 Ganesh Sittampalam-{-# LANGUAGE CPP, ViewPatterns #-}+{-# LANGUAGE ViewPatterns #-} module Darcs.Patch.Conflict ( Conflict(..), CommuteNoConflicts(..), listConflictedFiles , IsConflictedPrim(..), ConflictState(..)@@ -31,8 +31,6 @@ import Darcs.Util.Path ( FileName, fn2fp, fp2fn ) import Darcs.Util.Show ( appPrec ) -#include "impossible.h"- listConflictedFiles :: Conflict p => p wX wY -> [FilePath] listConflictedFiles p = nubSort $ concatMap (unseal listTouchedFiles) $ concat $ resolveConflicts p@@ -61,14 +59,32 @@ return $ ys' :> reverseRL rxs' instance (CommuteNoConflicts p, Conflict p) => Conflict (RL p) where+ -- By definition, a conflicting (primitive) patch is resolved if+ -- another (primitive) patch depends on the conflict.+ -- + -- So, when looking for conflicts in a list of patches, we go+ -- through the whole list looking for individual patches that are+ -- in conflict. But then we try to commute them past all the+ -- patches we've already seen. If we fail, i.e. there's something+ -- that depends on the conflict, then we forget about the conflict;+ -- this is the Nothing case of the 'commuteNoConflictsFL' call.+ --+ -- Note that 'primitive' does not mean Prim (this is a case of bad+ -- naming) but rather a RepoPatchV1 or RepoPatchV2. Prim patches+ -- are merely a 'base class' containing everything common to V1 and+ -- V2 primitive patches. resolveConflicts x = rcs x NilFL- where rcs :: RL p wX wY -> FL p wY wW -> [[Sealed (FL (PrimOf p) wW)]]- rcs NilRL _ = []- 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 (ps:<:p) passedby = seq passedby $ rcs ps (p:>:passedby)+ where+ rcs :: RL p wX wY -> FL p wY wW -> [[Sealed (FL (PrimOf p) wW)]]+ rcs NilRL _ = []+ rcs (ps :<: p) passedby+ | null (resolveConflicts p) = seq passedby rest -- TODO why seq here?+ | otherwise =+ case commuteNoConflictsFL (p :> passedby) of+ Just (_ :> p') -> resolveConflicts p' ++ rest+ Nothing -> rest+ where+ rest = rcs ps (p :>: passedby) conflictedEffect = concat . reverse . mapRL conflictedEffect instance CommuteNoConflicts p => CommuteNoConflicts (RL p) where
@@ -15,7 +15,7 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP , ScopedTypeVariables #-}+{-# LANGUAGE ScopedTypeVariables #-} module Darcs.Patch.Depends ( getUncovered@@ -28,8 +28,8 @@ , slightlyOptimizePatchset , getPatchesBeyondTag , splitOnTag- , newsetUnion- , newsetIntersection+ , patchSetUnion+ , patchSetIntersection , findUncommon , merge2FL , getDeps@@ -39,41 +39,35 @@ 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 ( 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+ , PatchChoices, unLabel, getChoices , LabelledPatch, label )-import Darcs.Patch.Commute ( commute, commuteFL, commuteRL )-import Darcs.Patch.Info ( PatchInfo, isTag, showPatchInfoUI, _piName )+import Darcs.Patch.Commute ( Commute, commute, commuteFL, commuteRL )+import Darcs.Patch.Info ( PatchInfo, isTag, displayPatchInfo, piName ) import Darcs.Patch.Merge ( Merge, mergeFL ) import Darcs.Patch.Permutations ( partitionFL, partitionRL ) import Darcs.Patch.PatchInfoAnd( PatchInfoAnd, hopefully, hopefullyM, info )-import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, newset2RL,+import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, patchSet2RL, appendPSFL ) import Darcs.Patch.Progress ( progressRL ) import Darcs.Patch.Witnesses.Eq ( EqCheck(..), (=\/=), (=/\=) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP, unsafeCoercePStart ) import Darcs.Patch.Witnesses.Ordered ( (:\/:)(..), (:/\:)(..), (:>)(..), Fork(..),- (+>+), mapFL, RL(..), FL(..), isShorterThanRL,+ (+>>+), (+<<+), mapFL, RL(..), FL(..), isShorterThanRL, (+<+), reverseFL, reverseRL, mapRL, lengthFL, splitAtFL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), FlippedSeal(..), flipSeal, seal, Sealed2(..), seal2 ) -import Darcs.Util.Printer ( renderString, vcat, RenderMode(..) )-import Darcs.Util.Tree ( Tree )+import Darcs.Util.Printer ( renderString, vcat ) {- - This module uses the following definitions:@@ -100,17 +94,17 @@ ) -- | Searchs dependencies in @repoFL@ of the patches in @getDepsFL@.-getDeps :: (RepoPatch p, ApplyState p ~ Tree) =>+getDeps :: (RepoPatch p) => 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+ getDepsFL' = mapFL (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 ->+ deps :: (Commute p) => PatchChoices (Named p) wX wY -> (String,Label) -> SPatchAndDeps p deps repoChoices (_,l) = case getChoices $ forceFirst l repoChoices of@@ -122,7 +116,7 @@ -- has r, which is the patch -- that we are looking at -- dependencies.- getLabelledDeps :: Patchy (Named p) => [String] ->+ getLabelledDeps :: (Commute p) => [String] -> PatchChoices (Named p) x y -> [(String, Label)] getLabelledDeps patchnames repoChoices = case getChoices repoChoices of@@ -138,7 +132,7 @@ else filterDepsFL patchnames lps where lpTostring :: LabelledPatch (Named p) wA wB -> String- lpTostring = BC.unpack . _piName . patch2patchinfo . lpPatch+ lpTostring = piName . patch2patchinfo . unLabel dep :: (String, Label) dep = lpTostring &&& label $ lp @@ -155,13 +149,13 @@ taggedIntersection does its best to reduce the number of inventories that are accessed from its rightmost argument. -}-taggedIntersection :: forall rt p wStart wX wY . Patchy p =>- PatchSet rt p wStart wX -> PatchSet rt p wStart wY ->+taggedIntersection :: forall rt p wStart wX wY . Commute 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 (PatchSet NilRL ps1) s2 = Fork NilRL ps1 (patchSet2RL s2)+taggedIntersection s1 (PatchSet NilRL ps2) = Fork NilRL (patchSet2RL s1) ps2 taggedIntersection s1 (PatchSet (_ :<: Tagged t _ _) ps2) | Just (PatchSet ts1 ps1) <- maybeSplitSetOnTag (info t) s1 = Fork ts1 ps1 (unsafeCoercePStart ps2)@@ -172,7 +166,7 @@ Just (PatchSet com NilRL :> us) -> Fork com us (unsafeCoercePStart ps2) Just _ -> impossible- Nothing -> Fork NilRL (newset2RL s1) (newset2RL s2)+ Nothing -> Fork NilRL (patchSet2RL s1) (patchSet2RL s2) -- |'maybeSplitSetOnTag' takes a tag's 'PatchInfo', @t0@, and a 'PatchSet' and -- attempts to find @t0@ in one of the 'Tagged's in the PatchSet. If the tag is@@ -188,7 +182,7 @@ Just $ PatchSet ts' (ps' +<+ ps) maybeSplitSetOnTag _ _ = Nothing -getPatchesBeyondTag :: Patchy p => PatchInfo -> PatchSet rt p wStart wX+getPatchesBeyondTag :: Commute p => PatchInfo -> PatchSet rt p wStart wX -> FlippedSeal (RL (PatchInfoAnd rt p)) wX getPatchesBeyondTag t (PatchSet (_ :<: Tagged hp _ _) ps) | info hp == t = flipSeal ps@@ -203,7 +197,7 @@ 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)+ bug $ "tag\n" ++ renderString (displayPatchInfo t) ++ "\nis not in the patchset in getPatchesBeyondTag." getPatchesBeyondTag t0 (PatchSet (ts :<: Tagged t _ ps) NilRL) = getPatchesBeyondTag t0 (PatchSet ts (ps :<: t))@@ -212,7 +206,7 @@ -- 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 => PatchInfo -> PatchSet rt p wStart wX+splitOnTag :: Commute 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.@@ -323,7 +317,7 @@ appendPSFL ps' (hp :>: NilFL) | otherwise = appendPSFL (sops $ PatchSet ts ps) (hp :>: NilFL) -removeFromPatchSet :: Patchy p => FL (PatchInfoAnd rt p) wX wY+removeFromPatchSet :: Commute 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@@ -332,14 +326,14 @@ removeFromPatchSet bad (PatchSet (ts :<: Tagged t _ tps) ps) = removeFromPatchSet bad (PatchSet ts (tps :<: t +<+ ps)) -fastRemoveSubsequenceRL :: Patchy p+fastRemoveSubsequenceRL :: Commute 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 rt p wStart wX wY . Patchy p+findCommonAndUncommon :: forall rt p wStart wX wY . Commute p => PatchSet rt p wStart wX -> PatchSet rt p wStart wY -> Fork (PatchSet rt p) (FL (PatchInfoAnd rt p))@@ -349,21 +343,21 @@ case partitionFL (infoIn them') $ reverseRL us' of _ :> bad@(_ :>: _) :> _ -> bug $ "Failed to commute common patches:\n"- ++ renderString Encode- (vcat $ mapRL (showPatchInfoUI . info) $ reverseFL bad)+ ++ renderString+ (vcat $ mapRL (displayPatchInfo . info) $ reverseFL bad) (common2 :> NilFL :> only_ours) -> case partitionFL (infoIn us') $ reverseRL them' of _ :> bad@(_ :>: _) :> _ -> bug $ "Failed to commute common patches:\n"- ++ renderString Encode (vcat $- mapRL (showPatchInfoUI . info) $ reverseFL bad)+ ++ renderString (vcat $+ mapRL (displayPatchInfo . info) $ reverseFL bad) _ :> NilFL :> only_theirs -> Fork (PatchSet common (reverseFL common2)) only_ours (unsafeCoercePStart only_theirs) where infoIn inWhat = (`elem` mapRL info inWhat) . info -findCommonWithThem :: Patchy p+findCommonWithThem :: Commute p => PatchSet rt p wStart wX -> PatchSet rt p wStart wY -> (PatchSet rt p :> FL (PatchInfoAnd rt p)) wStart wX@@ -372,12 +366,12 @@ case partitionFL ((`elem` mapRL info them') . info) $ reverseRL us' of _ :> bad@(_ :>: _) :> _ -> bug $ "Failed to commute common patches:\n"- ++ renderString Encode- (vcat $ mapRL (showPatchInfoUI . info) $ reverseFL bad)+ ++ renderString+ (vcat $ mapRL (displayPatchInfo . info) $ reverseFL bad) common2 :> _nilfl :> only_ours -> PatchSet common (reverseFL common2) :> unsafeCoerceP only_ours -findUncommon :: Patchy p+findUncommon :: Commute p => PatchSet rt p wStart wX -> PatchSet rt p wStart wY -> (FL (PatchInfoAnd rt p) :\/: FL (PatchInfoAnd rt p)) wX wY findUncommon us them =@@ -385,7 +379,7 @@ _common :> us' -> case findCommonWithThem them us of _ :> them' -> unsafeCoercePStart us' :\/: them' -countUsThem :: Patchy p+countUsThem :: Commute p => PatchSet rt p wStart wX -> PatchSet rt p wStart wY -> (Int, Int)@@ -395,7 +389,7 @@ tt = mapRL info them' in (length $ uu \\ tt, length $ tt \\ uu) -mergeThem :: (Patchy p, Merge p)+mergeThem :: (Merge p) => PatchSet rt p wStart wX -> PatchSet rt p wStart wY -> Sealed (FL (PatchInfoAnd rt p) wX) mergeThem us them =@@ -404,28 +398,28 @@ case merge2FL (reverseRL us') (reverseRL them') of them'' :/\: _ -> Sealed them'' -newsetIntersection :: Patchy p+patchSetIntersection :: Commute p => [SealedPatchSet rt p wStart] -> SealedPatchSet rt p wStart-newsetIntersection [] = seal $ PatchSet NilRL NilRL-newsetIntersection [x] = x-newsetIntersection (Sealed y : ys) =- case newsetIntersection ys of+patchSetIntersection [] = seal $ PatchSet NilRL NilRL+patchSetIntersection [x] = x+patchSetIntersection (Sealed y : ys) =+ case patchSetIntersection ys of Sealed z -> case taggedIntersection y z of 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 common commonps -newsetUnion :: (Patchy p, Merge p)+patchSetUnion :: (Merge p) => [SealedPatchSet rt p wStart] -> SealedPatchSet rt p wStart-newsetUnion [] = seal $ PatchSet NilRL NilRL-newsetUnion [x] = x-newsetUnion (Sealed y@(PatchSet tsy psy) : Sealed y2 : ys) =+patchSetUnion [] = seal $ PatchSet NilRL NilRL+patchSetUnion [x] = x+patchSetUnion (Sealed y@(PatchSet tsy psy) : Sealed y2 : ys) = case mergeThem y y2 of Sealed p2 ->- newsetUnion $ seal (PatchSet tsy (psy +<+ reverseFL p2)) : ys+ patchSetUnion $ 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@@ -440,7 +434,7 @@ -- (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)+merge2FL :: (Merge p) => FL (PatchInfoAnd rt p) wX wY -> FL (PatchInfoAnd rt p) wX wZ -> (FL (PatchInfoAnd rt p) :/\: FL (PatchInfoAnd rt p)) wY wZ@@ -454,7 +448,7 @@ ys'' :/\: xs' -> ys'' :/\: (x' :>: xs') -areUnrelatedRepos :: Patchy p+areUnrelatedRepos :: Commute p => PatchSet rt p wStart wX -> PatchSet rt p wStart wY -> Bool areUnrelatedRepos us them =@@ -477,12 +471,7 @@ -- 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+fastRemoveFL :: Commute 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)@@ -496,18 +485,18 @@ Just (b' :>: bs') where i = info a- pullout :: Patchy p+ pullout :: Commute 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)+ Just (x' :> acc' +>>+ xs) | otherwise = pullout (acc :<: x) xs -- | Same as 'fastRemoveFL' only for 'RL'.-fastRemoveRL :: Patchy p+fastRemoveRL :: Commute 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)@@ -521,12 +510,12 @@ Just (bs' :<: b') where i = info a- pullout :: Patchy p+ pullout :: Commute 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')+ Just (xs +<<+ acc' :> x') | otherwise = pullout xs (x :>: acc)
@@ -2,24 +2,28 @@ {-# LANGUAGE EmptyDataDecls #-} module Darcs.Patch.Dummy ( DummyPatch ) where +import Darcs.Patch.Annotate ( Annotate ) 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.Matchable ( Matchable )-import Darcs.Patch.Patchy- ( Patchy, ShowPatch, Invert, Commute, Apply(..), PatchInspect- , ReadPatch )-import Darcs.Patch.Prim ( FromPrim, PrimPatch, PrimPatchBase(..) )+import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute )+import Darcs.Patch.Invert ( Invert )+import Darcs.Patch.Inspect ( PatchInspect )+import Darcs.Patch.Read ( ReadPatch )+import Darcs.Patch.Show ( ShowPatch )+import Darcs.Patch.Prim ( FromPrim, PrimPatchCommon, PrimPatch, PrimPatchBase(..) ) import Darcs.Patch.Prim.Class ( PrimConstruct, PrimCanonize, PrimClassify , PrimDetails, PrimShow, PrimRead, PrimApply ) import Darcs.Patch.Merge ( Merge) import Darcs.Patch.Repair ( Check, RepairToFL ) import Darcs.Patch.RepoPatch ( RepoPatch )-import Darcs.Patch.Show ( ShowPatchBasic )-import Darcs.Patch.Witnesses.Eq ( MyEq )+import Darcs.Patch.Show ( ShowPatchBasic, ShowContextPatch )+import Darcs.Patch.Witnesses.Eq ( Eq2 ) import Darcs.Patch.Witnesses.Show ( Show2 ) import Darcs.Util.Tree( Tree ) @@ -29,15 +33,16 @@ instance IsHunk DummyPrim instance PatchListFormat DummyPrim-instance MyEq DummyPrim+instance Eq2 DummyPrim instance Invert DummyPrim instance PatchInspect DummyPrim instance ReadPatch DummyPrim instance ShowPatchBasic DummyPrim instance ShowPatch DummyPrim+instance ShowContextPatch DummyPrim instance Commute DummyPrim-instance Apply DummyPrim-instance Patchy DummyPrim+instance Apply DummyPrim where+ type ApplyState DummyPrim = Tree instance RepairToFL DummyPrim instance PrimConstruct DummyPrim@@ -52,20 +57,23 @@ instance PatchDebug DummyPrim +instance PrimPatchCommon DummyPrim+ instance IsHunk DummyPatch instance PatchListFormat DummyPatch-instance MyEq DummyPatch+instance Eq2 DummyPatch instance Invert DummyPatch instance PatchInspect DummyPatch instance ReadPatch DummyPatch instance ShowPatchBasic DummyPatch instance ShowPatch DummyPatch+instance ShowContextPatch DummyPatch instance Show2 DummyPatch instance Commute DummyPatch instance Apply DummyPatch where type ApplyState DummyPatch = Tree instance Matchable DummyPatch-instance Patchy DummyPatch+instance Annotate DummyPatch instance Effect DummyPatch instance Merge DummyPatch
@@ -1,7 +1,6 @@ module Darcs.Patch.Format ( PatchListFormat(..) , ListFormat(..)- , copyListFormat , FileNameFormat(..) ) where @@ -29,10 +28,8 @@ | ListFormatV2 -- ^ Show lists without braces. Read with arbitrary -- nested parens and flatten them out. -copyListFormat :: ListFormat p -> ListFormat q-copyListFormat ListFormatDefault = ListFormatDefault-copyListFormat ListFormatV1 = ListFormatV1-copyListFormat ListFormatV2 = ListFormatV2--data FileNameFormat = OldFormat- | NewFormat+data FileNameFormat+ = OldFormat -- ^ on-disk format for V1 patches+ | NewFormat -- ^ on-disk format for V2 patches+ | UserFormat -- ^ display format+ deriving (Eq, Show)
@@ -17,15 +17,21 @@ -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP,GeneralizedNewtypeDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} -module Darcs.Patch.Index.Monad ( FileModMonad, withPatchMods ) where+module Darcs.Patch.Index.Monad+ ( withPatchMods+ , applyToFileMods+ , makePatchID+ ) where import Prelude () import Darcs.Prelude -import Darcs.Patch.Index.Types ( PatchMod(..) )+import Darcs.Patch.Index.Types ( PatchMod(..), PatchId(..) )+import Darcs.Patch.Info ( makePatchname, PatchInfo )+import Darcs.Patch.Apply ( Apply(..) ) import Darcs.Patch.ApplyMonad ( ApplyMonad(..), ApplyMonadTree(..) ) import Control.Monad.State import Control.Arrow@@ -34,7 +40,6 @@ import Data.Set ( Set ) import Data.List ( isPrefixOf ) import Darcs.Util.Tree (Tree)-#include "impossible.h" newtype FileModMonad a = FMM (State (Set FileName, [PatchMod FileName]) a) deriving (Functor, Applicative, Monad, MonadState (Set FileName, [PatchMod FileName]))@@ -79,7 +84,6 @@ addMod (PRename fn newfn) mModifyFilePS f _ = addMod (PTouch f)- mModifyFilePSs f _ = addMod (PTouch f) -- --------------------------------------------------------------------- -- State Handling Functions@@ -117,3 +121,11 @@ modifyFps :: (Set FileName -> Set FileName) -> FileModMonad () modifyFps f = modify $ first f++makePatchID :: PatchInfo -> PatchId+makePatchID = PID . makePatchname++--------------------------------------------------------------------------------+-- | Apply a patch to set of 'FileName's, yielding the new set of 'FileName's and 'PatchMod's+applyToFileMods :: (Apply p, ApplyState p ~ Tree) => p wX wY -> Set FileName -> (Set FileName, [PatchMod FileName])+applyToFileMods patch = withPatchMods (apply patch)
@@ -21,9 +21,8 @@ import Prelude () import Darcs.Prelude -import Darcs.Util.Crypt.SHA1( SHA1(..) )-import Darcs.Util.Path ( fp2fn, fn2fp, FileName )-import Darcs.Patch.Info ( makePatchname, PatchInfo )+import Darcs.Util.Hash( SHA1, sha1short, sha1zero )+import Darcs.Util.Path ( fn2fp, FileName ) import Data.Binary ( Binary(..) ) import Data.Word ( Word32 ) @@ -40,10 +39,6 @@ (rfp,cnt) <- get return $ FileId rfp cnt --- | Parse FileId from a string-parseFileId :: String -> FileId-parseFileId s = let (f,'.':i) = break (=='.') s in FileId (fp2fn f) (read i)- -- | Convert FileId to string showFileId :: FileId -> String showFileId (FileId fn i) = show i++"#"++fn2fp fn@@ -59,12 +54,6 @@ pid2string :: PatchId -> String pid2string = show . patchId --- | describes a filepath that is interpreted relative to a certain--- point in the history of the repository. The point is given by--- Just pid which denotes the history up to (including) pid or--- Nothing which denotes the history including the last patch-data DatedFilePath = DatedFilePath FilePath (Maybe PatchId)- -- | This is used to track changes to files data PatchMod a = PTouch a | PCreateFile a@@ -79,9 +68,9 @@ -- track of them deriving (Show, Eq, Functor) -makePatchID :: PatchInfo -> PatchId-makePatchID = PID . makePatchname- short :: PatchId -> Word32-short (PID (SHA1 a _ _ _ _)) = a+short (PID sha1) = sha1short sha1++zero :: PatchId+zero = PID sha1zero
@@ -15,48 +15,77 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.Patch.Info ( PatchInfo(..), patchinfo, invertName,- rawPatchInfo,- addJunk, makePatchname,- makeFilename, readPatchInfo,- justName, justAuthor, justLog,- showPatchInfoUI, toXml, piDate,- setPiDate, piDateString, piDateBytestring,- piName, piRename, piAuthor, piTag, piLog,- showPatchInfo, isTag, readPatchInfos, escapeXML- ) where+module Darcs.Patch.Info+ ( PatchInfo(..) -- constructor and fields exported *only for tests*+ , rawPatchInfo -- exported *only for tests*+ , patchinfo+ , invertName+ , addJunk+ , makePatchname+ , readPatchInfo+ , justName+ , justAuthor+ , justLog+ , displayPatchInfo+ , toXml+ , toXmlShort+ , piDate+ , setPiDate+ , piDateString+ , piName+ , piRename+ , piAuthor+ , piTag+ , piLog+ , showPatchInfo+ , isTag+ , escapeXML+ , validDate+ , validLog+ , validAuthor+ , validDatePS+ , validLogPS+ , validAuthorPS+ ) where -import Prelude ()+import Prelude ( (^) ) import Darcs.Prelude +import Data.Char ( isAscii ) import System.Random ( randomRIO ) import Numeric ( showHex ) import Control.Monad ( when, unless, void ) -import Darcs.Util.ByteString ( unlinesPS, packStringToUTF8, unpackPSFromUTF8, decodeLocale)+import Darcs.Util.ByteString+ ( decodeLocale+ , packStringToUTF8+ , unlinesPS+ , unpackPSFromUTF8+ ) import qualified Darcs.Patch.ReadMonads as RM ( take ) import Darcs.Patch.ReadMonads as RM ( skipSpace, char, takeTill, anyChar, ParserM,- option, parseStrictly,+ option, takeTillChar, linesStartingWithEndingWith)+import Darcs.Patch.Show ( ShowPatchFor(..) ) import qualified Data.ByteString as B (length, splitAt, null ,isPrefixOf, tail, concat ,empty, head, cons, append ,ByteString )-import qualified Data.ByteString.Char8 as BC (index, head, unpack, pack)+import qualified Data.ByteString.Char8 as BC+ ( index, head, notElem, all, unpack, pack ) import Data.List( isPrefixOf ) import Darcs.Util.Printer ( Doc, packedString, empty, ($$), (<>), (<+>), vcat, text, cyanText, blueText, prefix )-import Darcs.Patch.OldDate ( readUTCDate, showIsoDateTime )-import System.Time ( CalendarTime(ctTZ), calendarTimeToString, toClockTime,+import Darcs.Util.IsoDate ( readUTCDate )+import System.Time ( CalendarTime, calendarTimeToString, toClockTime, toCalendarTime ) import System.IO.Unsafe ( unsafePerformIO )-import Darcs.Util.Crypt.SHA1 ( sha1PS, SHA1 )+import Darcs.Util.Hash ( sha1PS, SHA1 ) import Darcs.Util.Prompt ( promptYorn ) import Darcs.Util.Show ( appPrec )-import Prelude hiding (pi, log) -- | A PatchInfo value contains the metadata of a patch. The date, name, author -- and log fields are UTF-8 encoded text in darcs 2.4 and later, and just@@ -82,13 +111,47 @@ showString " " . showsPrec (appPrec + 1) log . showString " " . showsPrec (appPrec + 1) inverted +-- Validation++-- We need these functions to ensure that we can parse the+-- result of showPatchInfo.++validDate :: String -> Bool+validDate = all validCharForDate++validDatePS :: B.ByteString -> Bool+validDatePS = BC.all validCharForDate++-- | The isAscii limitation is due to the use of BC.pack below.+validCharForDate :: Char -> Bool+validCharForDate c = isAscii c && c /= '\n' && c /= ']'++validLog :: String -> Bool+validLog = notElem '\n'++validLogPS :: B.ByteString -> Bool+validLogPS = BC.notElem '\n'++validAuthor :: String -> Bool+validAuthor = notElem '*'++validAuthorPS :: B.ByteString -> Bool+validAuthorPS = BC.notElem '*'+ rawPatchInfo :: String -> String -> String -> [String] -> Bool -> PatchInfo rawPatchInfo date name author log inverted =- PatchInfo { _piDate = BC.pack date- , _piName = packStringToUTF8 name- , _piAuthor = packStringToUTF8 author- , _piLog = map packStringToUTF8 log+ PatchInfo { _piDate = BC.pack $ validateDate date+ , _piName = packStringToUTF8 $ validateName name+ , _piAuthor = packStringToUTF8 $ validateAuthor author+ , _piLog = map (packStringToUTF8 . validateLog) log , isInverted = inverted }+ where+ validateAuthor = validate validAuthor "author"+ validateName = validate validLog "patch name"+ validateLog = validate validLog "log line"+ validateDate = validate validDate "date"+ validate test meta x =+ if test x then x else error (unwords ["invalid",meta,show x]) -- | @patchinfo date name author log@ constructs a new 'PatchInfo' value -- with the given details, automatically assigning an Ignore-this header@@ -104,7 +167,8 @@ addJunk pinf = do x <- randomRIO (0,2^(128 ::Integer) :: Integer) when (_piLog pinf /= ignoreJunk (_piLog pinf)) $- do putStrLn "Lines beginning with 'Ignore-this: ' will be ignored."+ do putStrLn $ "Lines beginning with 'Ignore-this: ' " +++ "will not be shown when displaying a patch." confirmed <- promptYorn "Proceed? " unless confirmed $ fail "User cancelled because of Ignore-this." return $ pinf { _piLog = BC.pack (head ignored++showHex x ""):@@ -136,8 +200,8 @@ justLog :: PatchInfo -> String justLog = unlines . map BC.unpack . _piLog -showPatchInfoUI :: PatchInfo -> Doc-showPatchInfoUI pi =+displayPatchInfo :: PatchInfo -> Doc+displayPatchInfo pi = cyanText "patch " <> cyanText (show $ makePatchname pi) $$ text "Author: " <> text (piAuthor pi) $$ text "Date: " <> text (friendlyD $ _piDate pi)@@ -163,16 +227,12 @@ isTag :: PatchInfo -> Bool isTag pinfo = "TAG " `isPrefixOf` justName pinfo --- | Note: we ignore timezone information in the date string,--- systematically treating a time as UTC. So if the patch--- tells me it's 17:00 EST, we're actually treating it as--- 17:00 UTC, in other words 11:00 EST. This is for--- backwards compatibility to darcs prior to 2003-11, sometime--- before 1.0. Fortunately, newer patch dates are written in--- UTC, so this timezone truncation is harmless for them.+-- | Read the date from raw patch (meta) data and convert it to UTC.+-- The raw data may contain timezone info. This is for compatibiltity+-- with patches that were created before 2003-11, when darcs still+-- created patches that contained localized date strings. readPatchDate :: B.ByteString -> CalendarTime-readPatchDate = ignoreTz . readUTCDate . BC.unpack- where ignoreTz ct = ct { ctTZ = 0 }+readPatchDate = readUTCDate . BC.unpack piDate :: PatchInfo -> CalendarTime piDate = readPatchDate . _piDate@@ -180,9 +240,6 @@ piDateString :: PatchInfo -> String piDateString = BC.unpack . _piDate -piDateBytestring :: PatchInfo -> B.ByteString-piDateBytestring = _piDate- setPiDate :: String -> PatchInfo -> PatchInfo setPiDate date pi = pi { _piDate = BC.pack date } @@ -209,23 +266,31 @@ where bsUtf8 = unpackPSFromUTF8 bs friendlyD :: B.ByteString -> String---friendlyD d = calendarTimeToString . readPatchDate . d friendlyD d = unsafePerformIO $ do ct <- toCalendarTime $ toClockTime $ readPatchDate d return $ calendarTimeToString ct toXml :: PatchInfo -> Doc-toXml pi =+toXml = toXml' True++toXmlShort :: PatchInfo -> Doc+toXmlShort = toXml' False++toXml' :: Bool -> PatchInfo -> Doc+toXml' includeComments pi = text "<patch" <+> text "author='" <> escapeXMLByteString (_piAuthor pi) <> text "'" <+> text "date='" <> escapeXMLByteString (_piDate pi) <> text "'" <+> text "local_date='" <> escapeXML (friendlyD $ _piDate pi) <> text "'" <+> text "inverted='" <> text (show $ isInverted pi) <> text "'" <+> text "hash='" <> text (show $ makePatchname pi) <> text "'>"- $$ prefix "\t" (- text "<name>" <> escapeXMLByteString (_piName pi) <> text "</name>"- $$ commentsAsXml (_piLog pi))- $$ text "</patch>"+ $$ indent abstract+ $$ text "</patch>"+ where+ indent = prefix " "+ name = text "<name>" <> escapeXMLByteString (_piName pi) <> text "</name>"+ abstract | includeComments = name $$ commentsAsXml (_piLog pi)+ | otherwise = name commentsAsXml :: [B.ByteString] -> Doc commentsAsXml comments@@ -264,22 +329,6 @@ else B.cons (B.head bs) (bstrReplace c s (B.tail bs)) --- | This makes darcs-1 (non-hashed repos) filenames, and is also generally--- used in both in hashed and non-hashed repo code for making patch "hashes".------ The name consists of three segments:------ * timestamp (ISO8601-compatible yyyymmmddHHMMSS, UTC)------ * SHA1 hash of the author------ * SHA1 hash of the patch name, author, date, log, and \"inverted\"--- flag.-makeFilename :: PatchInfo -> String-makeFilename pi = showIsoDateTime d++"-"++sha1_a++"-"++ (show $ makePatchname pi) ++ ".gz"- where d = readPatchDate $ _piDate pi- sha1_a = take 5 $ show $ sha1PS $ _piAuthor pi- -- | Hash on patch metadata (patch name, author, date, log, and \"inverted\" -- flag. Robust against context changes but does not garantee patch contents. -- Usually used as matcher or patch identifier (see Darcs.Patch.Match).@@ -294,6 +343,10 @@ b2ps $ isInverted pi] +showPatchInfo :: ShowPatchFor -> PatchInfo -> Doc+showPatchInfo ForDisplay = displayPatchInfo+showPatchInfo ForStorage = storePatchInfo+ -- |Patch is stored between square brackets. -- -- > [ <patch name>@@ -306,8 +359,9 @@ -- -- note that below I assume the name has no newline in it. -- See 'readPatchInfo' for the inverse operation.-showPatchInfo :: PatchInfo -> Doc-showPatchInfo pi =+-- There are more assumptions, see validation functions above.+storePatchInfo :: PatchInfo -> Doc+storePatchInfo pi = blueText "[" <> packedString (_piName pi) $$ packedString (_piAuthor pi) <> text inverted <> packedString (_piDate pi) <> myunlines (_piLog pi) <> blueText "] "@@ -343,10 +397,3 @@ , _piLog = log , isInverted = BC.index s2 1 /= '*' }--readPatchInfos :: B.ByteString -> [PatchInfo]-readPatchInfos inv | B.null inv = []-readPatchInfos inv = case parseStrictly readPatchInfo inv of- Just (pinfo,r) -> pinfo : readPatchInfos r- _ -> []-
@@ -6,7 +6,8 @@ import Prelude () import Darcs.Prelude -import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..), reverseFL, reverseRL )+import Darcs.Patch.Witnesses.Ordered+ ( FL(..), RL(..), reverseFL, reverseRL, (:>)(..) ) class Invert p where@@ -25,3 +26,6 @@ instance Invert p => Invert (RL p) where invert = reverseFL . invertRL++instance Invert p => Invert (p :> p) where+ invert (a :> b) = invert b :> invert a
@@ -15,7 +15,7 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, ScopedTypeVariables, MultiParamTypeClasses, FlexibleInstances, Rank2Types #-}+{-# LANGUAGE ScopedTypeVariables, MultiParamTypeClasses, FlexibleInstances #-} -- | /First matcher, Second matcher and Nonrange matcher/ --@@ -47,11 +47,11 @@ , splitSecondFL , matchPatch , matchAPatch- , matchAPatchread , getNonrangeMatchS , firstMatch , secondMatch , haveNonrangeMatch+ , haveNonrangeExplicitMatch , havePatchsetMatch , checkMatchSyntax , applyInvToMatcher@@ -102,8 +102,7 @@ import Darcs.Util.Path ( AbsolutePath ) import Darcs.Patch- ( Patchy- , IsRepoType+ ( IsRepoType , hunkMatches , listTouchedFiles , invert@@ -125,11 +124,12 @@ 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.Set ( PatchSet(..), Tagged(..), SealedPatchSet, patchSet2RL, Origin ) import Darcs.Patch.Type ( PatchType(..) )-import Darcs.Patch.Apply( ApplyState )+import Darcs.Patch.Apply( Apply, ApplyState ) import Darcs.Patch.ApplyPatches( applyPatches ) import Darcs.Patch.Depends ( getPatchesBeyondTag, splitOnTag )+import Darcs.Patch.Invert( Invert ) import Darcs.Patch.Witnesses.Eq ( isIsEq ) import Darcs.Patch.Witnesses.Ordered ( RL(..), snocRLSealed, FL(..), (:>)(..) )@@ -142,7 +142,6 @@ import Darcs.Util.DateMatcher ( parseDateMatcher ) import Darcs.Util.Tree ( Tree )-#include "impossible.h" -- | A type for predicates over patches which do not care about -- contexts@@ -177,7 +176,7 @@ deriving ( Show ) -makeMatcher :: String -> (Sealed2 (PatchInfoAnd rt p) -> Bool) -> Matcher rt p+makeMatcher :: String -> MatchFun rt p -> Matcher rt p makeMatcher = MATCH -- | @applyMatcher@ applies a matcher to a patch.@@ -197,7 +196,7 @@ Left err -> error err Right m -> makeMatcher pattern m -addInternalMatcher :: (IsRepoType rt, Matchable p) => Maybe (Matcher rt p) -> Maybe (Matcher rt p)+addInternalMatcher :: (IsRepoType rt) => Maybe (Matcher rt p) -> Maybe (Matcher rt p) addInternalMatcher om = case namedInternalChecker of Nothing -> om@@ -218,16 +217,16 @@ -- When using <?>, Parsec prepends "expecting " to the given error message, -- so the phrasing below makes sense. helpfulErrorMsg = "valid expressions over: "- ++ intercalate ", " (map (\(name, _, _, _) -> name) ps)+ ++ intercalate ", " (map (\(name, _, _, _, _) -> name) ps) ++ "\nfor more help, see `darcs help patterns`." -- This type signature is just to bind an ambiguous type var.- ps :: [(String, String, [String], String -> MatchFun rt DummyPatch)]+ ps :: [(String, 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 rt p+ matchAnyPatch :: MatchFun rt p matchAnyPatch = const True submatch :: Matchable p => CharParser st (MatchFun rt p)@@ -258,16 +257,15 @@ where matchers_ = map createMatchHelper primitiveMatchers -createMatchHelper :: (String, String, [String], String -> MatchFun rt p)+createMatchHelper :: (String, String, String, [String], String -> MatchFun rt p) -> CharParser st (MatchFun rt p)-createMatchHelper (key,_,_,matcher) =+createMatchHelper (key,_,_,_,matcher) = do _ <- trystring key spaces q <- quoted return $ matcher q --- FIXME: would this be better defined in Darcs.Commands.Help?--- | The string that is emitted when the user runs @darcs help --match@.+-- | The string that is emitted when the user runs @darcs help patterns@. helpOnMatchers :: [String] helpOnMatchers = ["Selecting Patches:",@@ -291,45 +289,42 @@ ++ ["", "Here are some examples:", ""] ++ examples where -- This type signature exists to appease GHC.- ps :: [(String, String, [String], String -> MatchFun rt DummyPatch)]+ ps :: [(String, 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]+ keywords = [showKeyword (unwords [k,a]) d | (k,a,d,_,_) <- ps]+ examples = [showExample k e | (k,_,_,es,_) <- ps, e <- es] showKeyword keyword description =- -- FIXME: it would be nice to have a variable name here:- -- "author REGEX - match against author (email address)"- -- or "exact STRING - match against exact patch name".- "- " ++ keyword ++ " - " ++ description ++ "."+ " " ++ keyword ++ " - " ++ description ++ "." showExample keyword example =- " darcs log --match "+ " darcs log --match " ++ "'" ++ keyword ++ " " ++ example ++ "'" -primitiveMatchers :: Matchable p => [(String, String, [String], String -> MatchFun rt p)]- -- ^ keyword (operator), help description, list+primitiveMatchers :: Matchable p => [(String, String, String, [String], String -> MatchFun rt p)]+ -- ^ keyword (operator), argument name, help description, list -- of examples, matcher function primitiveMatchers =- [ ("exact", "check a literal string against the patch name"+ [ ("exact", "STRING", "check literal STRING is equal to patch name" , ["\"Resolve issue17: use dynamic memory allocation.\""] , exactmatch )- , ("name", "check a regular expression against the patch name"+ , ("name", "REGEX", "match REGEX against patch name" , ["issue17", "\"^[Rr]esolve issue17\\>\""] , namematch )- , ("author", "check a regular expression against the author name"+ , ("author", "REGEX", "match REGEX against patch author" , ["\"David Roundy\"", "droundy", "droundy@darcs.net"] , authormatch )- , ("hunk", "check a regular expression against the contents of a hunk patch"+ , ("hunk", "REGEX", "match REGEX against contents of a hunk patch" , ["\"foo = 2\"", "\"^instance .* Foo where$\""] , hunkmatch )- , ("comment", "check a regular expression against the log message"+ , ("comment", "REGEX", "match REGEX against the full log message" , ["\"prevent deadlocks\""] , logmatch )- , ("hash", "match a full hash or a prefix for a patch"+ , ("hash", "HASH", "match HASH against (a prefix of) the hash of a patch" , ["c719567e92c3b0ab9eddd5290b705712b8b918ef","c7195"] , hashmatch )- , ("date", "match the patch date"+ , ("date", "DATE", "match DATE against the patch date" , ["\"2006-04-02 22:41\"", "\"tea time yesterday\""] , datematch )- , ("touch", "match file paths for a patch"+ , ("touch", "REGEX", "match file paths for a patch" , ["src/foo.c", "src/", "\"src/*.(c|h)\""] , touchmatch ) ] @@ -346,7 +341,10 @@ <|> between spaces spaces (many $ noneOf " ()") <?> "string" -namematch, exactmatch, authormatch, hunkmatch, hashmatch, datematch, touchmatch+datematch, hashmatch, authormatch, exactmatch, namematch, logmatch+ :: String -> MatchFun rt p++hunkmatch, touchmatch :: Matchable p => String -> MatchFun rt p namematch r (Sealed2 hp) = isJust $ matchRegex (mkRegex r) $ justName (info hp)@@ -355,7 +353,6 @@ authormatch a (Sealed2 hp) = isJust $ matchRegex (mkRegex a) $ justAuthor (info hp) -logmatch :: Matchable p => String -> MatchFun rt p logmatch l (Sealed2 hp) = isJust $ matchRegex (mkRegex l) $ justLog (info hp) hunkmatch r (Sealed2 hp) = let regexMatcher = isJust . matchRegex (mkRegex r) . BC.unpack@@ -373,14 +370,31 @@ data InclusiveOrExclusive = Inclusive | Exclusive deriving Eq +data IncludeInternalPatches = IncludeInternalPatches | ExcludeInternalPatches+ deriving Eq+ -- | @haveNonrangeMatch flags@ tells whether there is a flag in -- @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 rt p . (IsRepoType rt, Matchable p) => PatchType rt p -> [MatchFlag] -> Bool-haveNonrangeMatch _ fs =+haveNonrangeMatch :: forall rt p . (IsRepoType rt, Matchable p)+ => PatchType rt p -> [MatchFlag] -> Bool+haveNonrangeMatch pt fs = haveNonrangeMatch' IncludeInternalPatches pt fs++-- | @haveNonrangeExplicitMatch flags@ is just like @haveNonrangeMatch flags@,+-- but ignores "internal matchers" used to mask "internal patches"+haveNonrangeExplicitMatch :: forall rt p . (IsRepoType rt, Matchable p)+ => PatchType rt p -> [MatchFlag] -> Bool+haveNonrangeExplicitMatch pt fs = haveNonrangeMatch' ExcludeInternalPatches pt fs++haveNonrangeMatch' :: forall rt p . (IsRepoType rt, Matchable p)+ => IncludeInternalPatches -> PatchType rt p -> [MatchFlag] -> Bool+haveNonrangeMatch' i _ fs = case hasIndexRange fs of Just (m,n) | m == n -> True; _ -> False- || isJust (nonrangeMatcher fs::Maybe (Matcher rt p))+ || isJust (nonrangeMatch::Maybe (Matcher rt p))+ where+ nonrangeMatch | i == IncludeInternalPatches = nonrangeMatcher fs+ | otherwise = nonrangeMatcherArgs fs -- | @havePatchsetMatch flags@ tells whether there is a "patchset -- match" in the flag list. A patchset match is @--match@ or@@ -438,11 +452,11 @@ secondMatch :: [MatchFlag] -> Bool secondMatch fs = isJust (secondMatcher fs::Maybe (Matcher rt DummyPatch)) || isJust (hasIndexRange fs) -unpullLastN :: (ApplyMonad (ApplyState p) m, MonadProgress m, Patchy p, IsRepoType rt)+unpullLastN :: (Apply p, Invert p, ApplyMonad (ApplyState p) m, MonadProgress m, IsRepoType rt) => PatchSet rt p wX wY -> Int -> m ()-unpullLastN repo n = applyInvRL `unsealFlipped` safetake n (newset2RL repo)+unpullLastN repo n = applyInvRL `unsealFlipped` safetake n (patchSet2RL repo) checkMatchSyntax :: [MatchFlag] -> IO () checkMatchSyntax opts =@@ -456,16 +470,16 @@ getMatchPattern (SeveralPattern m:_) = Just m getMatchPattern (_:fs) = getMatchPattern fs -tagmatch :: Matchable p => String -> Matcher rt p+tagmatch :: 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 rt p+patchmatch :: String -> Matcher rt p patchmatch r = makeMatcher ("patch-name "++r) (namematch r) -hashmatch' :: Matchable p => String -> Matcher rt p+hashmatch' :: String -> Matcher rt p hashmatch' r = makeMatcher ("hash "++r) (hashmatch r) @@ -535,18 +549,11 @@ secondMatcherIsTag (UpToTag _:_) = True secondMatcherIsTag (_:fs) = secondMatcherIsTag fs --- | @matchAPatchread fs p@ tells whether @p@ matches the matchers in--- the flags listed in @fs@.-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 :: (IsRepoType rt, Matchable p) => [MatchFlag] -> PatchInfoAnd rt p wX wY -> Bool matchAPatch fs p =- case nonrangeMatcher fs of+ case nonrangeMatcher fs of Nothing -> True Just m -> applyMatcher m p @@ -647,8 +654,8 @@ 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 m ps@ returns a prefix of @ps@+-- ending in a patch matching @m@, and calls 'error' if there is none. 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@@ -723,7 +730,7 @@ -- | @applyInvp@ tries to get the patch that's in a 'PatchInfoAnd -- patch', and to apply its inverse. If we fail to fetch the patch -- then we share our sorrow with the user.-applyInvp :: (Patchy p, ApplyMonad (ApplyState p) m) => PatchInfoAnd rt p wX wY -> m ()+applyInvp :: (Apply p, Invert p, ApplyMonad (ApplyState p) m) => PatchInfoAnd rt p wX wY -> m () applyInvp hp = apply (invert $ fromHopefully hp) where fromHopefully = conscientiously $ \e -> text "Sorry, patch not available:"@@ -739,5 +746,5 @@ 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 (ApplyState p) m, MonadProgress m, Patchy p) => RL (PatchInfoAnd rt p) wX wR -> m ()+applyInvRL :: (Apply p, Invert p, ApplyMonad (ApplyState p) m, MonadProgress m) => RL (PatchInfoAnd rt p) wX wR -> m () applyInvRL = applyPatches . invertRL -- this gives nicer feedback
@@ -5,7 +5,9 @@ module Darcs.Patch.Matchable ( Matchable ) where import Darcs.Patch.Inspect ( PatchInspect )-import Darcs.Patch.Patchy ( Patchy )+import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Invert ( Invert(..) ) -class (Patchy p, PatchInspect p)+class (Apply p, Commute p, Invert p, PatchInspect p) => Matchable p
@@ -5,24 +5,43 @@ -- Portability : portable module Darcs.Patch.Merge- (+ ( -- * Definitions Merge(..) , selfMerger , mergeFL+ , naturalMerge+ -- * Properties+ , prop_mergeSymmetric+ , prop_mergeCommute ) where -import Darcs.Patch.Commute ( Commute )+import Darcs.Patch.Commute ( Commute(..) ) import Darcs.Patch.CommuteFn ( MergeFn )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..), isIsEq ) import Darcs.Patch.Witnesses.Ordered ( (:\/:)(..) , (:/\:)(..) , FL(..) , RL+ , (:>)(..) , reverseFL , reverseRL ) --- | Things that can always be merged+-- | Things that can always be merged.+--+-- Instances should obey the following laws:+--+-- * Symmetry+--+-- prop> merge (p :\/: q) == q' :/\: p' <=> merge (q :\/: p) == p' :/\: q'+--+-- * MergesCommute+--+-- prop> merge (p :\/: q) == q' :/\: p' ==> commute (p :> q') == Just (q :> p')+--+-- that is, the two branches of a merge commute to each other class Commute p => Merge p where merge :: (p :\/: p) wX wY -> (p :/\: p) wX wY@@ -48,3 +67,35 @@ mergeFL (p :\/: (x :>: xs)) = case merge (p :\/: x) of x' :/\: p' -> case mergeFL (p' :\/: xs) of xs' :/\: p'' -> (x' :>: xs') :/\: p''++-- | The natural, non-conflicting merge.+naturalMerge :: (Invert p, Commute p)+ => (p :\/: p) wX wY -> Maybe ((p :/\: p) wX wY)+naturalMerge (p :\/: q) = do+ q' :> ip' <- commute (invert p :> q)+ -- TODO: find a small convincing example that demonstrates why+ -- it is necessary to do this extra check here+ _ <- commute (p :> q')+ return (q' :/\: invert ip')++prop_mergeSymmetric :: (Eq2 p, Merge p) => (p :\/: p) wX wY -> Bool+prop_mergeSymmetric (p :\/: q) =+ case merge (p :\/: q) of+ q' :/\: p' ->+ case merge (q :\/: p) of+ p'' :/\: q'' ->+ isIsEq (q' =\/= q'') && isIsEq (p' =\/= p'')++prop_mergeCommute :: (Eq2 p, Merge p) => (p :\/: p) wX wY -> Bool+prop_mergeCommute (p :\/: q) =+ case merge (p :\/: q) of+ q' :/\: p' ->+ case commute (p :> q') of+ Nothing -> False+ Just (q'' :> p'') ->+ isIsEq (q'' =\/= q) && isIsEq (p'' =/\= p')+ &&+ case commute (q :> p') of+ Nothing -> False+ Just (p'' :> q'') ->+ isIsEq (p'' =\/= p) && isIsEq (q'' =/\= q')
@@ -55,5 +55,5 @@ silentlyRunProgressActions :: Monad m => String -> [ProgressAction m ()] -> m () silentlyRunProgressActions _ = mapM_ paAction -instance (Functor m, Monad m) => MonadProgress (TM.TreeMonad m) where+instance (Monad m) => MonadProgress (TM.TreeMonad m) where runProgressActions = silentlyRunProgressActions
@@ -15,9 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}-- module Darcs.Patch.Named ( Named(..), infopatch,@@ -42,25 +39,30 @@ import Darcs.Patch.FileHunk ( IsHunk(..) ) import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Info ( PatchInfo, readPatchInfo, showPatchInfo, patchinfo,- showPatchInfoUI, makePatchname, invertName )+ piName, displayPatchInfo, makePatchname, invertName ) import Darcs.Patch.Merge ( Merge(..) )-import Darcs.Patch.Patchy ( Commute(..), Invert(..), Apply(..),- PatchInspect(..), ReadPatch(..) )-import Darcs.Patch.Prim ( PrimOf, PrimPatchBase )+import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Inspect ( PatchInspect(..) )+import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.Prim ( PrimPatchBase(..) ) import Darcs.Patch.ReadMonads ( ParserM, option, lexChar, choice, skipWhile, anyChar ) import Darcs.Patch.Repair ( mapMaybeSnd, Repair(..), RepairToFL, Check(..) )-import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..), showNamedPrefix )+import Darcs.Patch.Show+ ( ShowPatchBasic(..), ShowPatch(..), ShowContextPatch(..), ShowPatchFor(..) ) import Darcs.Patch.Summary ( plainSummary ) import Darcs.Patch.Viewing () -- for ShowPatch FL instances -import Darcs.Patch.Witnesses.Eq ( MyEq(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..) ) import Darcs.Patch.Witnesses.Ordered ( (:>)(..), (:\/:)(..), (:/\:)(..), FL, mapFL, mapFL_FL ) import Darcs.Patch.Witnesses.Sealed ( Sealed, mapSeal ) import Darcs.Patch.Witnesses.Show ( ShowDict(..), Show1(..), Show2(..) ) -import Darcs.Util.Printer ( ($$), (<+>), (<>), prefix, text, vcat )+import Darcs.Util.Printer+ ( Doc, ($$), (<+>), (<>), prefix, text, vcat, cyanText, blueText ) -- | The @Named@ type adds a patch info about a patch, that is a name. data Named p wX wY where@@ -149,7 +151,7 @@ fmapFL_Named :: (FL p wA wB -> FL q wC wD) -> Named p wA wB -> Named q wC wD fmapFL_Named f (NamedP i deps p) = NamedP i deps (f p) -instance (Commute p, MyEq p) => MyEq (Named p) where+instance (Commute p, Eq2 p) => Eq2 (Named p) where unsafeCompare (NamedP n1 d1 p1) (NamedP n2 d2 p2) = n1 == n2 && d1 == d2 && unsafeCompare p1 p2 @@ -195,23 +197,73 @@ instance Check p => Check (Named p) where isInconsistent (NamedP _ _ p) = isInconsistent p +-- ForStorage: note the difference between use of <> when there are+-- no explicit dependencies vs. <+> when there are+showNamedPrefix :: ShowPatchFor -> PatchInfo -> [PatchInfo] -> Doc -> Doc+showNamedPrefix f@ForStorage n [] p =+ showPatchInfo f n <> p+showNamedPrefix f@ForStorage n d p =+ showPatchInfo f n+ $$ blueText "<"+ $$ vcat (map (showPatchInfo f) d)+ $$ blueText ">"+ <+> p+showNamedPrefix f@ForDisplay n [] p =+ showPatchInfo f n+ $$ p+showNamedPrefix f@ForDisplay n d p =+ showPatchInfo f n+ $$ showDependencies ShowDepsVerbose d+ $$ p++data ShowDepsFormat = ShowDepsVerbose | ShowDepsSummary+ deriving (Eq)++showDependencies :: ShowDepsFormat -> [PatchInfo] -> Doc+showDependencies format deps = vcat (map showDependency deps)+ where+ showDependency d = mark+ <+> cyanText "patch"+ <+> cyanText (show (makePatchname d))+ $$ asterisk <+> text (piName d)+ mark | format == ShowDepsVerbose = blueText "depend"+ | otherwise = text "D"+ asterisk | format == ShowDepsVerbose = text "*"+ | otherwise = text " *"+ instance (PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (Named p) where- showPatch (NamedP n [] p) = showPatchInfo n <> showPatch p- showPatch (NamedP n d p) = showNamedPrefix n d <+> showPatch p+ showPatch f (NamedP n d p) = showNamedPrefix f n d $ showPatch f p -instance (Apply p, CommuteNoConflicts p, Conflict p, IsHunk p, PatchListFormat p,+instance (Apply p, IsHunk p, PatchListFormat p,+ ShowContextPatch p) => ShowContextPatch (Named p) where+ showContextPatch f (NamedP n d p) =+ showNamedPrefix f n d <$> showContextPatch f p++instance (CommuteNoConflicts p, Conflict p, PatchListFormat p, PrimPatchBase p, ShowPatch p) => ShowPatch (Named p) where- showContextPatch (NamedP n [] p) = showContextPatch p >>= return . (showPatchInfo n <>)- showContextPatch (NamedP n d p) = showContextPatch p >>= return . (showNamedPrefix n d <+>)- description (NamedP n _ _) = showPatchInfoUI n- summary p = description p $$ text "" $$- prefix " " (plainSummary p) -- this isn't summary because summary does the- -- wrong thing with (Named (FL p)) so that it can- -- get the summary of a sequence of named patches- -- right.+ description (NamedP n _ _) = displayPatchInfo n+ summary p@(NamedP _ ds _) =+ let+ indent = prefix " "+ deps | ds == [] = text ""+ | otherwise = text ""+ $$ indent (showDependencies ShowDepsSummary ds)+ in+ description p $$ deps $$ indent (plainSummary p)+ -- this isn't summary because summary+ -- does the wrong thing with+ -- (Named (FL p)) so that it can get+ -- the summary of a sequence of named+ -- patches right. summaryFL = vcat . mapFL summary- showNicely p@(NamedP _ _ pt) = description p $$- prefix " " (showNicely pt)+ showNicely p@(NamedP _ ds pt) =+ let+ indent = prefix " "+ deps | ds == [] = text ""+ | otherwise = text ""+ $$ indent (showDependencies ShowDepsVerbose ds)+ in+ description p <> deps $$ indent (showNicely pt) instance Show2 p => Show1 (Named p wX) where showDict1 = ShowDictClass
@@ -12,15 +12,16 @@ import Prelude () import Darcs.Prelude+import Data.Coerce ( coerce ) 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.Format ( PatchListFormat(..), ListFormat ) import Darcs.Patch.Info- ( PatchInfo, showPatchInfo, showPatchInfoUI, patchinfo+ ( PatchInfo, showPatchInfo, displayPatchInfo, patchinfo ) import Darcs.Patch.Invert ( Invert(..) ) import Darcs.Patch.Named ( Named(..), fmapFL_Named )@@ -31,8 +32,8 @@ ) 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.Prim ( FromPrim )+import Darcs.Patch.Prim.Class ( PrimPatchBase(..) ) import Darcs.Patch.Read ( ReadPatch(..) ) import qualified Darcs.Patch.Rebase.Container as Rebase ( Suspended(..)@@ -43,7 +44,7 @@ ( RepoType(..), IsRepoType(..), SRepoType(..) , RebaseType(..), RebaseTypeOf, SRebaseType(..) )-import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..) )+import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..), ShowContextPatch(..), ShowPatchFor(..) ) import Darcs.Patch.Witnesses.Eq ( EqCheck(..) ) import Darcs.Patch.Witnesses.Sealed ( mapSeal )@@ -210,27 +211,35 @@ instance (ShowPatchBasic p, PatchListFormat p) => ShowPatchBasic (WrappedNamed rt p) where - showPatch (NormalP n) = showPatch n- showPatch (RebaseP i s) = showPatchInfo i <> showPatch s+ showPatch f (NormalP n) = showPatch f n+ showPatch f (RebaseP i s) = showPatchInfo f i <> showPatch f s +instance ( ShowContextPatch p, PatchListFormat p, Apply p+ , PrimPatchBase p, IsHunk p+ )+ => ShowContextPatch (WrappedNamed rt p) where++ showContextPatch f (NormalP n) = showContextPatch f n+ showContextPatch f@ForDisplay (RebaseP i s) =+ fmap (showPatchInfo f i $$) $ return (showPatch f s)+ showContextPatch f@ForStorage (RebaseP i s) =+ fmap (showPatchInfo f i <>) $ return (showPatch f 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+ description (RebaseP i _) = displayPatchInfo i summary (NormalP n) = summary n- summary (RebaseP i _) = showPatchInfoUI i+ summary (RebaseP i _) = displayPatchInfo i summaryFL = vcat . mapFL summary showNicely (NormalP n) = showNicely n- showNicely (RebaseP i s) = showPatchInfoUI i $$+ showNicely (RebaseP i s) = displayPatchInfo i $$ prefix " " (showNicely s) instance PatchInspect p => PatchInspect (WrappedNamed rt p) where@@ -277,7 +286,7 @@ _ -> fmap (mapSeal NormalP) readPatch' instance PatchListFormat p => PatchListFormat (ReadRebasing p) where- patchListFormat = copyListFormat (patchListFormat :: ListFormat p)+ patchListFormat = coerce (patchListFormat :: ListFormat p) instance (ReadPatch p, PatchListFormat p, PrimPatchBase p) => ReadPatch (ReadRebasing p) where readPatch' =
@@ -1,351 +0,0 @@--- Copyright (C) 2003 Peter Simons--- Copyright (C) 2003,2008 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.---- This module is intended to provide backwards-compatibility in the parsing--- of darcs patches. In other words: don't change it, new features don't get--- added here. The only user should be Darcs.Patch.Info.--module Darcs.Patch.OldDate ( readUTCDate, showIsoDateTime ) where--import Prelude ( (^) )-import Darcs.Prelude--import Text.ParserCombinators.Parsec-import System.Time-import Data.Char ( toUpper, isDigit )-import Control.Monad ( liftM, liftM2 )-import qualified Data.ByteString.Char8 as B-import Data.Maybe ( fromMaybe )---- | Read/interpret a date string, assuming UTC if timezone--- is not specified in the string-readUTCDate :: String -> CalendarTime-readUTCDate = readDate 0--readDate :: Int -> String -> CalendarTime-readDate tz d =- case parseDate tz d of- Left e -> error e- Right ct -> ct--parseDate :: Int -> String -> Either String CalendarTime-parseDate tz d =- if length d >= 14 && B.all isDigit bd- then Right $- CalendarTime (readI $ B.take 4 bd)- (toEnum $ (+ (-1)) $ readI $ B.take 2 $ B.drop 4 bd)- (readI $ B.take 2 $ B.drop 6 bd) -- Day- (readI $ B.take 2 $ B.drop 8 bd) -- Hour- (readI $ B.take 2 $ B.drop 10 bd) -- Minute- (readI $ B.take 2 $ B.drop 12 bd) -- Second- 0 Sunday 0 -- Picosecond, weekday and day of year unknown- "GMT" 0 False- else let dt = do { x <- dateTime tz; eof; return x }- in case parse dt "" d of- Left e -> Left $ "bad date: "++d++" - "++show e- Right ct -> Right ct- where bd = B.pack (take 14 d)- readI s = fst $ fromMaybe (error "parseDate: invalid date") (B.readInt s)--showIsoDateTime :: CalendarTime -> String-showIsoDateTime ct = concat [ show $ ctYear ct- , twoDigit . show . (+1) . fromEnum $ ctMonth ct- , twoDigit . show $ ctDay ct- , twoDigit . show $ ctHour ct- , twoDigit . show $ ctMin ct- , twoDigit . show $ ctSec ct- ]- where twoDigit [] = undefined- twoDigit x@(_:[]) = '0' : x- twoDigit x@(_:_:[]) = x- twoDigit _ = undefined------- Parser Combinators ------------------------------------------------- |Case-insensitive variant of Parsec's 'char' function.--caseChar :: Char -> GenParser Char a Char-caseChar c = satisfy (\x -> toUpper x == toUpper c)---- |Case-insensitive variant of Parsec's 'string' function.--caseString :: String -> GenParser Char a ()-caseString cs = mapM_ caseChar cs <?> cs---- |Match a parser at least @n@ times.--manyN :: Int -> GenParser a b c -> GenParser a b [c]-manyN n p- | n <= 0 = return []- | otherwise = liftM2 (++) (count n p) (many p)---- |Match a parser at least @n@ times, but no more than @m@ times.--manyNtoM :: Int -> Int -> GenParser a b c -> GenParser a b [c]-manyNtoM n m p- | n < 0 = return []- | n > m = return []- | n == m = count n p- | n == 0 = foldr ((<|>) . (\x -> try $ count x p)) (return []) $ reverse [1..m]- | otherwise = liftM2 (++) (count n p) (manyNtoM 0 (m-n) p)-------- Date/Time Parser -------------------------------------------------dateTime :: Int -> CharParser a CalendarTime-dateTime tz =- choice [try $ cvsDateTime tz,- try $ iso8601DateTime tz,- oldDateTime]--dayAndHMSTime :: CharParser a (Int, Int, Int, Int)-dayAndHMSTime = do d <- day- _ <- mySpaces- h <- hour- _ <- char ':'- m <- minute- _ <- char ':'- s <- second- return (d, h, m, s)--cvsDateTime :: Int -> CharParser a CalendarTime-cvsDateTime tz =- do y <- year- _ <- char '/'- mon <- monthNum- _ <- char '/'- (d, h, m, s) <- dayAndHMSTime- z <- option tz $ mySpaces >> zone- return (CalendarTime y mon d h m s 0 Monday 0 "" z False)--oldDateTime :: CharParser a CalendarTime-oldDateTime = do wd <- dayName- _ <- mySpaces- mon <- monthName- _ <- mySpaces- (d, h, m, s) <- dayAndHMSTime- _ <- mySpaces- z <- zone- _ <- mySpaces- y <- year- return (CalendarTime y mon d h m s 0 wd 0 "" z False)--{- FIXME: In case you ever want to use this outside of darcs, you should note- that this implementation of ISO 8601 is not complete.-- reluctant to implement (ambiguous!):- * years > 9999- * truncated representations with implied century (89 for 1989)- unimplemented:- * repeated durations (not relevant)- * lowest order component fractions in intervals- * negative dates (BC)- unverified or too relaxed:- * the difference between 24h and 0h- * allows stuff like 2005-1212; either you use the hyphen all the way- (2005-12-12) or you don't use it at all (20051212), but you don't use- it halfway, likewise with time- * No bounds checking whatsoever on intervals!- (next action: read iso doc to see if bounds-checking required?) -}-iso8601DateTime :: Int -> CharParser a CalendarTime-iso8601DateTime localTz = try $- do d <- iso8601Date- t <- option id $ try $ do optional $ oneOf " T"- iso8601Time- return $ t $ d { ctTZ = localTz }--iso8601Date :: CharParser a CalendarTime-iso8601Date =- do d <- calendar_date <|> week_date <|> ordinal_date- return $ foldr ($) nullCalendar d- where- calendar_date = -- yyyy-mm-dd- try $ do d <- optchain year_ [ (dash, month_), (dash, day_) ]- -- allow other variants to be parsed correctly- notFollowedBy (digit <|> char 'W')- return d- week_date = --yyyy-Www-dd- try $ do yfn <- year_- optional dash- _ <- char 'W'- -- offset human 'week 1' -> computer 'week 0'- w' <- (\x -> x-1) `liftM` twoDigits- wd <- option 1 $ do { optional dash; nDigits 1 }- let y = yfn nullCalendar- firstDay = ctWDay y- -- things that make this complicated- -- 1. iso8601 weeks start from Monday; Haskell weeks start from Sunday- -- 2. the first week is the one that contains at least Thursday- -- if the year starts after Thursday, then some days of the year- -- will have already passed before the first week- let afterThursday = firstDay == Sunday || firstDay > Thursday- w = if afterThursday then w'+1 else w'- diff c = c { ctDay = (7 * w) + wd - fromEnum firstDay }- return [toUTCTime.toClockTime.diff.yfn]- ordinal_date = -- yyyy-ddd- try $ optchain year_ [ (dash, yearDay_) ]- --- year_ = try $ do y <- fourDigits <?> "year (0000-9999)"- return $ \c -> c { ctYear = y }- month_ = try $ do m <- twoDigits <?> "month (1 to 12)"- -- we (artificially) use ctPicosec to indicate- -- whether the month has been specified.- return $ \c -> c { ctMonth = intToMonth m, ctPicosec = 0 }- day_ = try $ do d <- twoDigits <?> "day in month (1 to 31)"- return $ \c -> c { ctDay = d }- yearDay_ = try $ do d <- nDigits 3 <?> "day in year (1 to 366)"- return $ \c -> c { ctYDay = d }- dash = char '-'---- we return a function which sets the time on another calendar-iso8601Time :: CharParser a (CalendarTime -> CalendarTime)-iso8601Time = try $- do ts <- optchain hour_ [ (colon , min_)- , (colon , sec_)- , (oneOf ",.", pico_) ]- z <- option id $ choice [ zulu , offset ]- return $ foldr (.) id (z:ts)- where- hour_ = do h <- twoDigits- return $ \c -> c { ctHour = h }- min_ = do m <- twoDigits- return $ \c -> c { ctMin = m }- sec_ = do s <- twoDigits- return $ \c -> c { ctSec = s }- pico_ = do digs <- many digit- let picoExp = 12- digsExp = length digs- let frac | null digs = 0- | digsExp > picoExp = read $ take picoExp digs- | otherwise = 10 ^ (picoExp - digsExp) * read digs- return $ \c -> c { ctPicosec = frac }- zulu = do { _ <- char 'Z'; return (\c -> c { ctTZ = 0 }) }- offset = do sign <- choice [ char '+' >> return 1- , char '-' >> return (-1) ]- h <- twoDigits- m <- option 0 $ do { optional colon; twoDigits }- return $ \c -> c { ctTZ = sign * 60 * ((h*60)+m) }- colon = char ':'--optchain :: CharParser a b -> [(CharParser a c, CharParser a b)] -> CharParser a [b]-optchain p next = try $- do r1 <- p- r2 <- case next of- [] -> return []- ((sep,p2):next2) -> option [] $ do { optional sep; optchain p2 next2 }- return (r1:r2)--nDigits :: Int -> CharParser a Int-nDigits n = read `liftM` count n digit--twoDigits, fourDigits :: CharParser a Int-twoDigits = nDigits 2-fourDigits = nDigits 4--mySpaces :: CharParser a String-mySpaces = manyN 1 $ char ' '--dayName :: CharParser a Day-dayName = choice- [ caseString "Mon" >> return Monday- , try (caseString "Tue") >> return Tuesday- , caseString "Wed" >> return Wednesday- , caseString "Thu" >> return Thursday- , caseString "Fri" >> return Friday- , try (caseString "Sat") >> return Saturday- , caseString "Sun" >> return Sunday- ]--year :: CharParser a Int-year = fourDigits--monthNum :: CharParser a Month-monthNum = do mn <- manyNtoM 1 2 digit- return $ intToMonth (read mn :: Int)--intToMonth :: Int -> Month-intToMonth 1 = January-intToMonth 2 = February-intToMonth 3 = March-intToMonth 4 = April-intToMonth 5 = May-intToMonth 6 = June-intToMonth 7 = July-intToMonth 8 = August-intToMonth 9 = September-intToMonth 10 = October-intToMonth 11 = November-intToMonth 12 = December-intToMonth _ = error "invalid month!"--monthName :: CharParser a Month-monthName = choice- [ try (caseString "Jan") >> return January- , caseString "Feb" >> return February- , try (caseString "Mar") >> return March- , try (caseString "Apr") >> return April- , caseString "May" >> return May- , try (caseString "Jun") >> return June- , caseString "Jul" >> return July- , caseString "Aug" >> return August- , caseString "Sep" >> return September- , caseString "Oct" >> return October- , caseString "Nov" >> return November- , caseString "Dec" >> return December- ]--day :: CharParser a Int-day = do d <- manyNtoM 1 2 digit- return (read d :: Int)--hour :: CharParser a Int-hour = twoDigits--minute :: CharParser a Int-minute = twoDigits--second :: CharParser a Int-second = twoDigits--zone :: CharParser a Int-zone = choice- [ do { _ <- char '+'; h <- hour; m <- minute; return (((h*60)+m)*60) }- , do { _ <- char '-'; h <- hour; m <- minute; return (-((h*60)+m)*60) }- , mkZone "UTC" 0- , mkZone "UT" 0- , mkZone "GMT" 0- , mkZone "EST" (-5)- , mkZone "EDT" (-4)- , mkZone "CST" (-6)- , mkZone "CDT" (-5)- , mkZone "MST" (-7)- , mkZone "MDT" (-6)- , mkZone "PST" (-8)- , mkZone "PDT" (-7)- , mkZone "CEST" 2- , mkZone "EEST" 3- -- if we don't understand it, just give a GMT answer...- , do { _ <- manyTill (oneOf $ ['a'..'z']++['A'..'Z']++" ")- (lookAhead space_digit);- return 0 }- ]- where mkZone n o = try $ do { caseString n; return (o*60*60) }- space_digit = try $ do { _ <- char ' '; oneOf ['0'..'9'] }--nullCalendar :: CalendarTime-nullCalendar = CalendarTime 0 January 0 0 0 0 1 Sunday 0 "" 0 False
@@ -15,9 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}-- module Darcs.Patch.PatchInfoAnd ( Hopefully(..), SimpleHopefully(..), PatchInfoAnd(..), WPatchInfo, unWPatchInfo, compareWPatchInfo, piap, n2pia, patchInfoAndPatch,@@ -34,8 +31,8 @@ import Darcs.Util.SignalHandler ( catchNonSignal ) import Darcs.Util.Printer ( Doc, renderString, errorDoc, text, ($$), vcat- , RenderMode(..) )-import Darcs.Patch.Info ( PatchInfo, showPatchInfoUI, justName )+ )+import Darcs.Patch.Info ( PatchInfo, showPatchInfo, displayPatchInfo, justName ) import Darcs.Patch.Conflict ( Conflict, CommuteNoConflicts ) import Darcs.Patch.Debug ( PatchDebug(..) ) import Darcs.Patch.Effect ( Effect(..) )@@ -47,12 +44,16 @@ , generaliseRepoTypeWrapped ) import Darcs.Patch.Prim ( PrimPatchBase(..), FromPrim )-import Darcs.Patch.Patchy ( Patchy, ReadPatch(..), Apply(..), Invert(..),- ShowPatch(..), Commute(..), PatchInspect(..) )+import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Inspect ( PatchInspect(..) )+import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.Show ( ShowPatch(..) ) 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.Show ( ShowPatchBasic(..), ShowContextPatch(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..), EqCheck(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) import Darcs.Patch.Witnesses.Ordered ( (:>)(..), (:\/:)(..), (:/\:)(..), FL, mapFL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), seal, mapSeal )@@ -67,7 +68,9 @@ -- 'SimpleHopefully' does the real work of emulating -- 'Either'. @Hopefully sh@ represents an expected unhashed patch, and -- @Hashed hash sh@ represents an expected hashed patch with its hash.-data Hopefully a wX wY = Hopefully (SimpleHopefully a wX wY) | Hashed String (SimpleHopefully a wX wY)+data Hopefully a wX wY+ = Hopefully (SimpleHopefully a wX wY)+ | Hashed String (SimpleHopefully a wX wY) deriving Show -- | @SimpleHopefully@ is a variant of @Either String@ adapted for@@ -101,7 +104,7 @@ compareWPatchInfo :: WPatchInfo wA wB -> WPatchInfo wC wD -> EqCheck (wA, wB) (wC, wD) compareWPatchInfo (WPatchInfo x) (WPatchInfo y) = if x == y then unsafeCoerceP IsEq else NotEq -instance MyEq WPatchInfo where+instance Eq2 WPatchInfo where WPatchInfo x `unsafeCompare` WPatchInfo y = x == y fmapH :: (a wX wY -> b wW wZ) -> Hopefully a wX wY -> Hopefully b wW wZ@@ -160,15 +163,15 @@ conscientiously er (PIAP pinf hp) = case hopefully2either hp of Right p -> p- Left e -> errorDoc $ er (showPatchInfoUI pinf $$ text e)+ Left e -> errorDoc $ er (displayPatchInfo pinf $$ text e) -- | @hopefullyM@ is a version of @hopefully@ which calls @fail@ in a -- monad instead of erroring. 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- (showPatchInfoUI pinf $$ text e)+ Left e -> fail $ renderString+ (displayPatchInfo pinf $$ text e) -- Any recommendations for a nice adverb to name the below? hopefully2either :: Hopefully a wX wY -> Either String (a wX wY)@@ -197,7 +200,7 @@ -- 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 rt p) where+instance Eq2 (PatchInfoAnd rt p) where unsafeCompare (PIAP i _) (PIAP i2 _) = i == i2 instance Invert p => Invert (PatchInfoAnd rt p) where@@ -206,23 +209,27 @@ instance PatchListFormat (PatchInfoAnd rt p) 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+ showPatch f (PIAP n p) =+ case hopefully2either p of+ Right x -> showPatch f x+ Left _ -> showPatchInfo f n +instance (Apply p, IsHunk p, PatchListFormat p, PrimPatchBase p,+ ShowContextPatch p) => ShowContextPatch (PatchInfoAnd rt p) where+ showContextPatch f (PIAP n p) = case hopefully2either p of+ Right x -> showContextPatch f x+ Left _ -> return $ showPatchInfo f n+ instance (Apply p, Conflict p, CommuteNoConflicts p, IsHunk p, PatchListFormat p, PrimPatchBase p, 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- description (PIAP n _) = showPatchInfoUI n+ description (PIAP n _) = displayPatchInfo n summary (PIAP n p) = case hopefully2either p of Right x -> summary x- Left _ -> showPatchInfoUI n+ Left _ -> displayPatchInfo n summaryFL = vcat . mapFL summary showNicely (PIAP n p) = case hopefully2either p of Right x -> showNicely x- Left _ -> showPatchInfoUI n+ Left _ -> displayPatchInfo n instance Commute p => Commute (PatchInfoAnd rt p) where commute (x :> y) = do y' :> x' <- commute (hopefully x :> hopefully y)@@ -260,6 +267,3 @@ isHunk _ = Nothing instance PatchDebug p => PatchDebug (PatchInfoAnd rt p)--instance (Patchy p, ApplyState p ~ Tree) => Patchy (PatchInfoAnd rt p)-
@@ -1,36 +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.--{-# LANGUAGE CPP #-}---module Darcs.Patch.Patchy- ( Patchy,- Apply(..), Commute(..), Invert(..),- PatchInspect(..), ReadPatch(..),- showPatch, ShowPatch(..)- ) where--import Darcs.Patch.Apply ( Apply(..) )-import Darcs.Patch.Commute ( Commute(..) )-import Darcs.Patch.Invert ( Invert(..) )-import Darcs.Patch.Inspect ( PatchInspect(..) )-import Darcs.Patch.Read ( ReadPatch(..) )-import Darcs.Patch.Show ( showPatch, ShowPatch(..) )--class (Apply p, Commute p, Invert p) => Patchy p-
@@ -1,13 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Darcs.Patch.Patchy.Instances () where--import Darcs.Patch.Format ( PatchListFormat )-import Darcs.Patch.Patchy ( Patchy )-import Darcs.Patch.Permutations ()-import Darcs.Patch.FileHunk ( IsHunk )-import Darcs.Patch.Viewing ()-import Darcs.Patch.Witnesses.Ordered ( FL, RL )--instance (IsHunk p, PatchListFormat p, Patchy p) => Patchy (FL p)-instance (IsHunk p, PatchListFormat p, Patchy p) => Patchy (RL p)
@@ -17,9 +17,7 @@ -- Boston, MA 02110-1301, USA. {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP #-} - module Darcs.Patch.Permutations ( removeFL, removeRL, removeCommon, commuteWhatWeCanFL, commuteWhatWeCanRL, genCommuteWhatWeCanRL, genCommuteWhatWeCanFL,@@ -38,12 +36,11 @@ import Darcs.Patch.Commute ( Commute, commute, commuteFLorComplain, commuteRL ) import Darcs.Patch.CommuteFn ( CommuteFn ) import Darcs.Patch.Invert ( Invert(..) )-import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..), EqCheck(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..), (:>)(..), (+<+) , reverseFL, (+>+), (:\/:)(..), lengthFL , lengthRL, reverseRL )-#include "impossible.h" -- |split an 'FL' into "left" and "right" lists according to a predicate @p@, using commutation as necessary. -- If a patch does satisfy the predicate but cannot be commuted past one that does not satisfy@@ -134,11 +131,11 @@ genCommuteWhatWeCanRL _ (NilRL :> y) = NilRL :> y :> NilRL -removeCommon :: (MyEq p, Commute p) => (FL p :\/: FL p) wX wY -> (FL p :\/: FL p) wX wY+removeCommon :: (Eq2 p, Commute p) => (FL p :\/: FL p) wX wY -> (FL p :\/: FL p) wX wY removeCommon (xs :\/: NilFL) = xs :\/: NilFL removeCommon (NilFL :\/: xs) = NilFL :\/: xs removeCommon (xs :\/: ys) = rc xs (headPermutationsFL ys)- where rc :: (MyEq p, Commute p) => FL p wX wY -> [(p:>FL p) wX wZ] -> (FL p :\/: FL p) wY wZ+ where rc :: (Eq2 p, Commute p) => FL p wX wY -> [(p:>FL p) wX wZ] -> (FL p :\/: FL p) wY wZ rc nms ((n:>ns):_) | Just ms <- removeFL n nms = removeCommon (ms :\/: ns) rc ms [n:>ns] = ms :\/: n:>:ns rc ms (_:nss) = rc ms nss@@ -146,17 +143,17 @@ -- | 'removeFL' @x xs@ removes @x@ from @xs@ if @x@ can be commuted to its head. -- Otherwise it returns 'Nothing'-removeFL :: (MyEq p, Commute p) => p wX wY -> FL p wX wZ -> Maybe (FL p wY wZ)+removeFL :: (Eq2 p, Commute p) => p wX wY -> FL p wX wZ -> Maybe (FL p wY wZ) removeFL x xs = r x $ headPermutationsFL xs- where r :: (MyEq p, Commute p) => p wX wY -> [(p:>FL p) wX wZ] -> Maybe (FL p wY wZ)+ where r :: (Eq2 p, Commute p) => p wX wY -> [(p:>FL p) wX wZ] -> Maybe (FL p wY wZ) r _ [] = Nothing r z ((z':>zs):zss) | IsEq <- z =\/= z' = Just zs | otherwise = r z zss -- | 'removeRL' is like 'removeFL' except with 'RL'-removeRL :: (MyEq p, Commute p) => p wY wZ -> RL p wX wZ -> Maybe (RL p wX wY)+removeRL :: (Eq2 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)+ where r :: (Eq2 p, Commute p) => p wY wZ -> [RL p wX wZ] -> Maybe (RL p wX wY) r z ((zs:<:z'):zss) | IsEq <- z =/\= z' = Just zs | otherwise = r z zss r _ _ = Nothing@@ -165,21 +162,21 @@ -- @ab@ have been commuted out of it, if possible. If this is not possible -- for any reason (the set of patches @ab@ is not actually a subset of @abc@, -- or they can't be commuted out) we return 'Nothing'.-removeSubsequenceFL :: (MyEq p, Commute p) => FL p wA wB+removeSubsequenceFL :: (Eq2 p, Commute p) => FL p wA wB -> FL p wA wC -> Maybe (FL p wB wC) removeSubsequenceFL a b | lengthFL a > lengthFL b = Nothing | otherwise = rsFL a b- where rsFL :: (MyEq p, Commute p) => FL p wA wB -> FL p wA wC -> Maybe (FL p wB wC)+ where rsFL :: (Eq2 p, Commute p) => FL p wA wB -> FL p wA wC -> Maybe (FL p wB wC) rsFL NilFL ys = Just ys rsFL (x:>:xs) yys = removeFL x yys >>= removeSubsequenceFL xs -- | 'removeSubsequenceRL' is like @removeSubsequenceFL@ except that it works -- on 'RL'-removeSubsequenceRL :: (MyEq p, Commute p) => RL p wAb wAbc+removeSubsequenceRL :: (Eq2 p, Commute p) => RL p wAb wAbc -> RL p wA wAbc -> Maybe (RL p wA wAb) removeSubsequenceRL a b | lengthRL a > lengthRL b = Nothing | otherwise = rsRL a b- where rsRL :: (MyEq p, Commute p) => RL p wAb wAbc -> RL p wA wAbc -> Maybe (RL p wA wAb)+ where rsRL :: (Eq2 p, Commute p) => RL p wAb wAbc -> RL p wA wAbc -> Maybe (RL p wA wAb) rsRL NilRL ys = Just ys rsRL (xs:<:x) yys = removeRL x yys >>= removeSubsequenceRL xs @@ -222,7 +219,7 @@ Just $ xs:<:p1':<:p2' swapfirstRL _ = Nothing -instance (MyEq p, Commute p) => MyEq (FL p) where+instance (Eq2 p, Commute p) => Eq2 (FL p) where a =\/= b | lengthFL a /= lengthFL b = NotEq | otherwise = cmpSameLength a b where cmpSameLength :: FL p wX wY -> FL p wX wZ -> EqCheck wY wZ@@ -231,7 +228,7 @@ cmpSameLength _ _ = NotEq xs =/\= ys = reverseFL xs =/\= reverseFL ys -instance (MyEq p, Commute p) => MyEq (RL p) where+instance (Eq2 p, Commute p) => Eq2 (RL p) where unsafeCompare = bug "Buggy use of unsafeCompare on RL" a =/\= b | lengthRL a /= lengthRL b = NotEq | otherwise = cmpSameLength a b
@@ -1,25 +1,24 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Patch.Prim- ( showPrim, showPrimFL,- primIsAddfile, primIsHunk, primIsBinary, primIsSetpref,- primIsAdddir, is_filepatch,- canonize, tryToShrink,- sortCoalesceFL, coalesce, canonizeFL,- tryShrinkingInverse,- summarizePrim,- applyPrimFL,- readPrim,- FromPrim(..), FromPrims(..), ToFromPrim(..),- PrimPatch, PrimPatchBase(..), PrimConstruct(..)- )- where+ ( primIsAddfile, primIsHunk, primIsBinary, primIsSetpref+ , primIsAdddir, is_filepatch+ , summarizePrim+ , applyPrimFL+ , PrimRead(..)+ , PrimShow(..)+ , FromPrim(..), FromPrims(..), ToFromPrim(..)+ , PrimPatch, PrimPatchBase(..)+ , PrimConstruct(..)+ , PrimCanonize(..)+ , PrimPatchCommon+ ) where import Darcs.Patch.Prim.Class ( PrimConstruct(..), PrimCanonize(..) , PrimClassify(..), PrimDetails(..)- , PrimShow(..), showPrimFL, PrimRead(..)+ , PrimShow(..), PrimRead(..) , PrimApply(..) , FromPrim(..), FromPrims(..), ToFromPrim(..) , PrimPatchBase(..), PrimPatch+ , PrimPatchCommon )-
@@ -1,10 +1,11 @@ module Darcs.Patch.Prim.Class ( PrimConstruct(..), PrimCanonize(..) , PrimClassify(..), PrimDetails(..)- , PrimShow(..), showPrimFL, PrimRead(..)+ , PrimShow(..), PrimRead(..) , PrimApply(..) , PrimPatch, PrimPatchBase(..) , FromPrim(..), FromPrims(..), ToFromPrim(..)+ , PrimPatchCommon ) where @@ -15,34 +16,52 @@ import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.FileHunk ( FileHunk, IsHunk ) import Darcs.Util.Path ( FileName )-import Darcs.Patch.Format ( PatchListFormat, FileNameFormat(..) )+import Darcs.Patch.Format ( FileNameFormat, PatchListFormat ) import Darcs.Patch.Inspect ( PatchInspect )-import Darcs.Patch.Patchy ( Patchy )+import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Invert ( Invert(..) ) import Darcs.Patch.Read ( ReadPatch ) import Darcs.Patch.ReadMonads ( ParserM ) import Darcs.Patch.Repair ( RepairToFL )-import Darcs.Patch.Show ( ShowPatch )+import Darcs.Patch.Show ( ShowPatch, ShowContextPatch ) import Darcs.Patch.SummaryData ( SummDetail )-import Darcs.Patch.Witnesses.Eq ( MyEq(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..) ) import Darcs.Patch.Witnesses.Ordered- ( FL(..), RL, (:>), mapFL, mapFL_FL, reverseFL )+ ( FL(..), RL, (:>), mapFL_FL, reverseFL ) import Darcs.Patch.Witnesses.Show ( Show2 ) import Darcs.Patch.Witnesses.Sealed ( Sealed ) -import Darcs.Util.Printer ( Doc, vcat )+import Darcs.Util.Printer ( Doc ) import qualified Darcs.Util.Diff as D ( DiffAlgorithm ) import qualified Data.ByteString as B ( ByteString ) -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+-- | This class describes the abstract interface to primitive patches+-- that is indepenent of the on-disk format.+class ( Apply prim+ , Commute prim+ , Invert prim+ , Eq2 prim+ , IsHunk prim+ , PatchInspect prim+ , RepairToFL prim+ , Show2 prim+ , PrimConstruct prim+ , PrimCanonize prim+ , PrimClassify prim+ , PrimDetails prim+ , PrimApply prim )+ => PrimPatchCommon prim++class ( PrimPatchCommon prim+ , ReadPatch prim+ , ShowPatch prim+ , ShowContextPatch prim+ , PatchListFormat prim+ ) => PrimPatch prim class PrimPatch (PrimOf p) => PrimPatchBase p where@@ -55,7 +74,7 @@ type PrimOf (RL p) = PrimOf p class FromPrim p where- fromPrim :: PrimOf p wX wY -> p wX wY+ fromPrim :: PrimOf p wX wY -> p wX wY class FromPrim p => ToFromPrim p where toPrim :: p wX wY -> Maybe (PrimOf p wX wY)@@ -143,9 +162,7 @@ class PrimShow prim where showPrim :: FileNameFormat -> prim wA wB -> Doc--showPrimFL :: PrimShow prim => FileNameFormat -> FL prim wA wB -> Doc-showPrimFL f xs = vcat (mapFL (showPrim f) xs)+ showPrimCtx :: ApplyMonad (ApplyState prim) m => FileNameFormat -> prim wA wB -> m Doc class PrimRead prim where readPrim :: ParserM m => FileNameFormat -> m (Sealed (prim wX))
@@ -1,9 +1,6 @@ {-# 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 ()@@ -12,11 +9,10 @@ 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 )+import Darcs.Patch.Prim.Class ( PrimPatchCommon, PrimPatch, PrimPatchBase(..), FromPrim(..) ) +instance PrimPatchCommon Prim instance PrimPatch Prim-instance Patchy Prim instance PrimPatchBase Prim where type PrimOf Prim = Prim
@@ -1,81 +1,98 @@-{-# LANGUAGE CPP, MultiParamTypeClasses, OverloadedStrings #-}+{-# LANGUAGE MultiParamTypeClasses, OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans -fno-warn-missing-methods #-}-module Darcs.Patch.Prim.FileUUID.Apply ( ObjectMap(..) ) where+module Darcs.Patch.Prim.FileUUID.Apply ( hunkEdit, 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.ByteString as B import qualified Data.Map as M --- import Darcs.Patch.ApplyMonad ( ApplyMonad(..) )+import Debug.Trace ( trace )+-- import Text.Show.Pretty ( ppShow ) +import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.ApplyMonad+ ( ApplyMonad(..), ApplyMonadTrans(..)+ , ToTree(..), ApplyMonadState(..)+ )+import Darcs.Patch.Prim.Class ( PrimApply(..) )+import Darcs.Patch.Prim.FileUUID.Core ( Prim(..), Hunk(..), HunkMove(..) )+import Darcs.Patch.Prim.FileUUID.Show+import Darcs.Patch.Prim.FileUUID.ObjectMap+import Darcs.Patch.Repair ( RepairToFL(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(..) )+ import Darcs.Util.Hash( Hash(..) )+import Darcs.Util.Printer( text, packedString, ($$), renderString ) -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"+ type ApplyState Prim = ObjectMap+ apply (Manifest i (L dirid name)) = editDirectory dirid (M.insert name i)+ apply (Demanifest _ (L dirid name)) = editDirectory dirid (M.delete name)+ apply (Hunk i hunk) = editFile i (hunkEdit hunk)+ apply (HunkMove (HM fs ls ft lt c)) =+ editFile fs (hunkEdit (H ls c B.empty)) >> editFile ft (hunkEdit (H lt B.empty c))+ apply Identity = return () instance RepairToFL Prim where- applyAndTryToFixFL p = apply p >> return Nothing+ applyAndTryToFixFL p = apply p >> return Nothing instance PrimApply Prim where- applyPrimFL NilFL = return ()- applyPrimFL (p:>:ps) = apply p >> applyPrimFL ps+ 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 ()+hunkEdit :: Hunk wX wY -> FileContent -> FileContent+hunkEdit h@(H off old new) c+ | old `B.isPrefixOf` (B.drop off c) =+ B.concat [B.take off c, new, B.drop (off + B.length old) c]+ | otherwise = error $ renderString $+ text "##error applying hunk:" $$ displayHunk Nothing h $$ "##to" $$+ packedString c+-- $$ text "##old=" <> text (ppShow old) $$+-- text "##new=" <> text (ppShow new) $$+-- text "##c=" <> text (ppShow c) +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 +-- a semantic, ObjectMap-based interface for patch application class ApplyMonadObjectMap m where- -- a semantic, ObjectMap-based interface for patch application- editFile :: UUID -> (B.ByteString -> B.ByteString) -> m ()- editDirectory :: UUID -> (DirContent -> DirContent) -> m ()+ editFile :: UUID -> (FileContent -> FileContent) -> m ()+ editDirectory :: UUID -> (DirContent -> DirContent) -> m () instance ApplyMonadState ObjectMap where- type ApplyMonadStateOperations ObjectMap = ApplyMonadObjectMap+ type ApplyMonadStateOperations ObjectMap = ApplyMonadObjectMap -instance (Functor m, Monad m) => ApplyMonad ObjectMap (StateT (ObjectMap m) m) where- type ApplyMonadBase (StateT (ObjectMap m) m) = m+instance (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 (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' Nothing = Blob (return $ edit "") NoHash+ edit' (Just d@(Directory m)) =+ trace ("\neditFile called with Directory object: " ++ show (i,m) ++ "\n") d+ editDirectory i edit = editObject i edit'+ where+ edit' (Just (Directory x)) = Directory $ edit x+ edit' Nothing = Directory $ edit M.empty+ edit' (Just b@(Blob _ h)) =+ trace ("\neditDirectory called with File object: " ++ show (i,h) ++ "\n") b -instance (Functor m, Monad m) => ApplyMonadTrans ObjectMap m where+instance (Monad m) => ApplyMonadTrans ObjectMap m where type ApplyMonadOver ObjectMap m = StateT (ObjectMap m) m runApplyMonad = runStateT-
@@ -1,73 +1,62 @@-{-# OPTIONS_GHC -fno-warn-orphans -fno-warn-overlapping-patterns #-}-{-# LANGUAGE CPP #-}-module Darcs.Patch.Prim.FileUUID.Commute- ( CommuteMonad(..) )- where+{-# OPTIONS_GHC -fno-warn-orphans #-}+module Darcs.Patch.Prim.FileUUID.Commute () where import Prelude () import Darcs.Prelude -import Data.List ( intersect )--import qualified Data.ByteString as BS (length)+import qualified Data.ByteString as B (length) import Darcs.Patch.Witnesses.Ordered ( (:>)(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )-import Darcs.Patch.Prim.FileUUID.Core ( Prim(..), Hunk(..), touches )+import Darcs.Patch.Prim.FileUUID.Core ( Prim(..), Hunk(..) ) 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+-- For FileUUID it is easier to list the cases that do /not/ commute+depends :: (Prim :> Prim) wX wY -> Bool+depends (Manifest i1 l1 :> Demanifest i2 l2)+ -- cannot commute add with remove of same object, regardless of location+ | i1 == i2 = True+ -- cannot commute add with remove of any two things at the same location+ | l1 == l2 = True+depends (Demanifest i1 l1 :> Manifest i2 l2)+ -- cannot commute remove with add of same object, regardless of location+ | i1 == i2 = True+ -- cannot commute remove with add of any two things at the same location+ | l1 == l2 = True+depends (_ :> _) = False 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+ commute pair+ | depends pair = Nothing+ commute (Hunk f1 h1 :> Hunk f2 h2)+ | f1 == f2 =+ case commuteHunk (h1 :> h2) of+ Just (h2' :> h1') -> Just (Hunk f2 h2' :> Hunk f1 h1')+ Nothing -> Nothing+ commute (a :> b) =+ Just (unsafeCoerceP b :> unsafeCoerceP a) -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+commuteHunk :: (Hunk :> Hunk) wX wY -> Maybe ((Hunk :> Hunk) wX wY)+commuteHunk (H off1 old1 new1 :> H off2 old2 new2)+ | off1 + len_new1 < off2 = yes (off2 - len_new1 + len_old1, off1)+ | off2 + len_old2 < off1 = yes (off2, off1 + len_new2 - len_old2)+ | len_old2 /= 0+ , len_old1 /= 0+ , len_new2 /= 0+ , len_new1 /= 0+ , off1 + len_new1 == off2 = yes (off2 - len_new1 + len_old1, off1)+ | len_old2 /= 0+ , len_old1 /= 0+ , len_new2 /= 0+ , len_new1 /= 0+ , off2 + len_old2 == off1 = yes (off2, off1 + len_new2 - len_old2)+ | otherwise = no+ where+ len_old1 = B.length old1+ len_new1 = B.length new1+ len_old2 = B.length old2+ len_new2 = B.length new2+ yes (off2', off1') = Just (H off2' old2 new2 :> H off1' old1 new1)+ no = Nothing
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, OverloadedStrings, StandaloneDeriving #-}+{-# LANGUAGE OverloadedStrings, StandaloneDeriving #-} -- Copyright (C) 2011 Petr Rockai --@@ -24,128 +24,127 @@ module Darcs.Patch.Prim.FileUUID.Core- ( Prim(..), Hunk(..), UUID(..), Location, Object(..), touches, hunkEdit )- where+ ( Prim(..)+ , Hunk(..)+ , HunkMove(..)+ -- re-exports+ , Object(..)+ , UUID(..)+ , Location(..)+ , Name+ , FileContent+ ) where import Prelude () import Darcs.Prelude -import qualified Data.ByteString as BS--import Darcs.Patch.Witnesses.Eq ( MyEq(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..) ) import Darcs.Patch.Witnesses.Show ( Show1(..), Show2(..), ShowDict(ShowDictClass) )+import Darcs.Patch.Witnesses.Unsafe 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+-- -----------------------------------------------------------------------------+-- Hunk -data Hunk wX wY where- Hunk :: !Int -> BS.ByteString -> BS.ByteString -> Hunk wX wY- deriving Show+data Hunk wX wY = H !Int !FileContent !FileContent+ deriving (Eq, Show) instance Show1 (Hunk wX) where- showDict1 = ShowDictClass+ showDict1 = ShowDictClass instance Show2 Hunk where- showDict2 = ShowDictClass+ showDict2 = ShowDictClass invertHunk :: Hunk wX wY -> Hunk wY wX-invertHunk (Hunk off old new) = Hunk off new old+invertHunk (H off old new) = H 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 Eq2 Hunk where+ unsafeCompare p q = unsafeCoerceP p == q -instance MyEq Hunk where- unsafeCompare (Hunk i x y) (Hunk i' x' y') = i == i' && x == x' && y == y'+-- -----------------------------------------------------------------------------+-- HunkMove -data Prim wX wY where- BinaryHunk :: !UUID -> Hunk wX wY -> Prim wX wY- TextHunk :: !UUID -> Hunk wX wY -> Prim wX wY+data HunkMove wX wY = HM !UUID !Int !UUID !Int !FileContent+ deriving (Eq, Show) - -- 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+invertHunkMove :: HunkMove wX wY -> HunkMove wY wX+invertHunkMove (HM sid soff tid toff content) = HM tid toff sid soff content +instance Eq2 HunkMove where+ unsafeCompare (HM sid1 soff1 tid1 toff1 c1) (HM sid2 soff2 tid2 toff2 c2) =+ sid1 == sid2 && soff1 == soff2 && tid1 == tid2 && toff1 == toff2 && c1 == c2++-- -----------------------------------------------------------------------------+-- Prim++data Prim wX wY where+ Hunk :: !UUID -> !(Hunk wX wY) -> Prim wX wY+ HunkMove :: !(HunkMove wX wY) -> Prim wX wY+ Manifest :: !UUID -> !Location -> Prim wX wY+ Demanifest :: !UUID -> !Location -> Prim wX wY+ Identity :: Prim wX wX++deriving instance Eq (Prim wX wY) deriving instance Show (Prim wX wY) instance Show1 (Prim wX) where- showDict1 = ShowDictClass+ 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 = []+ showDict2 = ShowDictClass -- 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+ 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+ 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+ 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+ invert (Hunk x h) = Hunk x $ invertHunk h+ invert (HunkMove hm) = HunkMove $ invertHunkMove hm+ invert (Manifest x y) = Demanifest x y+ invert (Demanifest x y) = Manifest x 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+ -- We don't need this for FileUUID. Slashes are not allowed in Manifest and+ -- Demanifest patches and nothing else uses working-copy paths.+ listTouchedFiles _ = [] -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+ -- TODO (used for --match 'hunk ...', presumably)+ hunkMatches _ _ = False -instance Eq (Prim wX wY) where- (==) = unsafeCompare+instance Eq2 Prim where+ unsafeCompare (Hunk a b) (Hunk 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
@@ -20,24 +20,47 @@ -- 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+module Darcs.Patch.Prim.FileUUID.ObjectMap+ ( UUID(..), Location(..), Object(..)+ , ObjectMap(..), DirContent, FileContent+ , isBlob, isDirectory+ , Name -- re-export+ ) where import Prelude () import Darcs.Prelude -import Darcs.Util.Hash( Hash )-import qualified Data.ByteString as BS (ByteString)+import Darcs.Util.Hash ( Hash )+import Darcs.Util.Path ( Name )+import qualified Data.ByteString as B (ByteString) import qualified Data.Map as M +type FileContent = B.ByteString -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+newtype UUID = UUID B.ByteString deriving (Eq, Ord, Show) -data ObjectMap (m :: * -> *) = ObjectMap { getObject :: UUID -> m (Maybe (Object m))- , putObject :: UUID -> Object m -> m (ObjectMap m)- , listObjects :: m [UUID]- }+-- | An object is located by giving the 'UUID' of the parent+-- 'Directory' and a 'Name'.+data Location = L !UUID !Name+ deriving (Eq, Show)++-- TODO use HashMap instead?+type DirContent = M.Map Name UUID++data Object (m :: * -> *)+ = Directory DirContent+ | Blob (m FileContent) !Hash++isBlob :: Object m -> Bool+isBlob Blob{} = True+isBlob Directory{} = False++isDirectory :: Object m -> Bool+isDirectory Directory{} = True+isDirectory Blob{} = False++data ObjectMap (m :: * -> *) = ObjectMap+ { getObject :: UUID -> m (Maybe (Object m))+ , putObject :: UUID -> Object m -> m (ObjectMap m)+ , listObjects :: m [UUID]+ }
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, ViewPatterns, OverloadedStrings #-}+{-# LANGUAGE ViewPatterns, OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Patch.Prim.FileUUID.Read () where @@ -8,39 +8,43 @@ 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.Core( Prim(..), Hunk(..), Location(..) ) import Darcs.Patch.Prim.FileUUID.ObjectMap import Darcs.Patch.Witnesses.Sealed( seal )+import Darcs.Util.Path ( unsafeMakeName ) 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)+ readPrim _ = do+ skipSpace+ choice $ map (liftM seal)+ [ identity+ , hunk "hunk" Hunk+ , 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 = unsafeMakeName . decodeWhite <$> myLex'+ content = do+ lexString "content"+ len <- int+ _ <- char '\n'+ Darcs.Patch.ReadMonads.take len+ location = liftM2 L uuid filename+ hunk kind ctor = do+ uid <- patch kind+ offset <- int+ old <- content+ new <- content+ return $ ctor uid (H offset old new) instance ReadPatch Prim where readPatch' = readPrim undefined
@@ -1,70 +1,118 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP, OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings #-} module Darcs.Patch.Prim.FileUUID.Show- ( showHunk )+ ( displayHunk ) 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.Show+ ( ShowPatchBasic(..), ShowPatch(..)+ , ShowContextPatch(..), ShowPatchFor(..) )+import Darcs.Patch.Summary ( plainSummaryPrim ) import Darcs.Patch.Prim.Class ( PrimShow(..) )-import Darcs.Patch.Prim.FileUUID.Core ( Prim(..), Hunk(..), UUID(..) )+import Darcs.Patch.Prim.FileUUID.Core+ ( Prim(..), Hunk(..), HunkMove(..), UUID(..), Location(..), FileContent ) import Darcs.Patch.Prim.FileUUID.Details ()-import Darcs.Util.Printer ( text, packedString, blueText, (<+>), (<>), Doc )--#include "impossible.h"+import Darcs.Util.ByteString ( linesPS )+import Darcs.Util.Path ( Name, encodeWhiteName )+import Darcs.Util.Printer+ ( text, packedString, blueText, prefix+ , (<+>), (<>), ($$), Doc, vcat+ ) -- TODO this instance shouldn't really be necessary, as Prims aren't used generically instance PatchListFormat Prim +fileNameFormat :: ShowPatchFor -> FileNameFormat+fileNameFormat ForDisplay = UserFormat+fileNameFormat ForStorage = NewFormat+ instance ShowPatchBasic Prim where- showPatch = showPrim OldFormat+ showPatch fmt = showPrim (fileNameFormat fmt) +-- dummy instance, does not actually show any context+instance ShowContextPatch Prim where+ -- showContextPatch f = showPrimCtx (fileNameFormat f)+ showContextPatch f p = return $ showPatch f p+ instance ShowPatch Prim where- showContextPatch p = return $ showPatch p- summary = plainSummaryPrim- summaryFL = plainSummaryPrims False []- thing _ = "change"+ 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 UserFormat (Hunk u h) = displayHunk (Just u) h+ showPrim _ (Hunk u h) = storeHunk u h+ showPrim UserFormat (HunkMove hm) = displayHunkMove hm+ showPrim _ (HunkMove hm) = storeHunkMove hm+ showPrim _ (Manifest f (L d p)) = showManifest "manifest" d f p+ showPrim _ (Demanifest f (L d p)) = showManifest "demanifest" d f p showPrim _ Identity = blueText "identity"- showPrim _ (Move{}) = bug "show for move not implemented"+ showPrimCtx _ _ = bug "show with context not implemented" -showManifest :: String -> UUID -> UUID -> BC.ByteString -> Doc-showManifest txt dir file path = blueText txt <+>- formatUUID file <+>- formatUUID dir <+>- packedString (encodeWhite path)+showManifest :: String -> UUID -> UUID -> Name -> Doc+showManifest txt dir file name =+ blueText txt <+>+ formatUUID file <+>+ formatUUID dir <+>+ packedString (encodeWhiteName name) -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)+displayHunk :: Maybe UUID -> Hunk wX wY -> Doc+displayHunk uid (H off old new) =+ blueText "hunk" <+>+ maybe (text "<nil>") formatUUID uid <+>+ text (show off) $$+ displayFileContent "-" old $$+ displayFileContent "+" new -formatUUID :: UUID -> Doc-formatUUID (UUID x) = packedString x+storeHunk :: UUID -> Hunk wX wY -> Doc+storeHunk uid (H off old new) =+ text "hunk" <+>+ formatUUID uid <+>+ text (show off) $$+ storeFileContent old $$+ storeFileContent new --- 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+displayHunkMove :: HunkMove wX wY -> Doc+displayHunkMove (HM sid soff tid toff c) =+ blueText "hunkmove" <+>+ formatUUID sid <+>+ text (show soff) <+>+ formatUUID tid <+>+ text (show toff) $$+ displayFileContent "|" c +storeHunkMove :: HunkMove wX wY -> Doc+storeHunkMove (HM sid soff tid toff c) =+ text "hunkmove" <+>+ formatUUID sid <+>+ text (show soff) <+>+ formatUUID tid <+>+ text (show toff) $$+ storeFileContent c++-- TODO add some heuristics to recognize binary content+displayFileContent :: String -> FileContent -> Doc+displayFileContent pre = vcat . map (prefix pre) . showLines . linesPS+ where+ context = blueText "[...]"+ showLines [] = []+ showLines [x]+ | B.null x = []+ | otherwise = [context <> packedString x <> context]+ showLines (x:xs) =+ [context <> packedString x] +++ map packedString (init xs) +++ [packedString (last xs) <> context]++storeFileContent :: FileContent -> Doc+storeFileContent c =+ text "content" <+> text (show (B.length c)) $$ packedString c++formatUUID :: UUID -> Doc+formatUUID (UUID x) = packedString x
@@ -1,9 +1,6 @@ {-# 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 ()@@ -12,13 +9,6 @@ import Darcs.Patch.Prim.V1.Read () import Darcs.Patch.Prim.V1.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+import Darcs.Patch.Prim.Class ( PrimPatchCommon ) -instance FromPrim Prim where- fromPrim = id+instance PrimPatchCommon Prim
@@ -1,5 +1,4 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP #-} module Darcs.Patch.Prim.V1.Apply () where import Prelude ()@@ -14,24 +13,23 @@ DirPatchType(..), FilePatchType(..) ) import Darcs.Patch.Prim.V1.Show ( showHunk ) -import Darcs.Util.Path ( fn2fp )-import Darcs.Patch.Format ( FileNameFormat(..) )-import Darcs.Patch.TokenReplace ( tryTokInternal )+import Darcs.Util.Path ( FileName, fn2fp )+import Darcs.Patch.Format ( FileNameFormat(UserFormat) )+import Darcs.Patch.TokenReplace ( tryTokReplace ) -import Darcs.Patch.ApplyMonad ( ApplyMonad(..), ApplyMonadTree(..) )+import Darcs.Patch.ApplyMonad ( ApplyMonadTree(..) ) import Darcs.Util.Tree( Tree ) import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL_FL, spanFL, (:>)(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoercePStart ) -import Darcs.Util.ByteString ( unlinesPS, breakAfterNthNewline, breakBeforeNthNewline, )-import Darcs.Util.Printer( renderString, RenderMode(..) )+import Darcs.Util.ByteString ( unlinesPS )+import Darcs.Util.Printer( renderString ) +import qualified Data.ByteString as B+import qualified Data.ByteString.Internal as BI import qualified Data.ByteString as B ( ByteString, empty, null, concat )-import qualified Data.ByteString.Char8 as BC (pack, singleton, unpack)-import Data.List ( intersperse )--#include "impossible.h"+import qualified Data.ByteString.Char8 as BC (pack, unpack, unlines) type FileContents = B.ByteString @@ -39,13 +37,13 @@ type ApplyState Prim = Tree apply (FP f RmFile) = mRemoveFile f apply (FP f AddFile) = mCreateFile f- apply p@(FP _ (Hunk{})) = applyPrimFL (p :>: NilFL)- apply (FP f (TokReplace t o n)) = mModifyFilePSs f doreplace- where doreplace ls =- case mapM (tryTokInternal t (BC.pack o) (BC.pack n)) ls of+ apply (FP f (Hunk l o n)) = mModifyFilePS f $ applyHunk f (l, o, n)+ apply (FP f (TokReplace t o n)) = mModifyFilePS f doreplace+ where doreplace fc =+ case tryTokReplace t (BC.pack o) (BC.pack n) fc of Nothing -> fail $ "replace patch to " ++ fn2fp f ++ " couldn't apply."- Just ls' -> return $ map B.concat ls'+ Just fc' -> return fc' apply (FP f (Binary o n)) = mModifyFilePS f doapply where doapply oldf = if o == oldf then return n@@ -96,61 +94,129 @@ do let foo = h :>: mapFL_FL (\(FP _ h') -> h') xs mModifyFilePS f $ hunkmod foo applyPrimFL ps'- where f_hunk (FP f' (Hunk{})) | f == f' = True+ where f_hunk (FP f' (Hunk{})) = f == f' f_hunk _ = False- hunkmod :: ApplyMonad Tree m => FL FilePatchType wX wY+ -- TODO there should be a HOF that abstracts+ -- over this recursion scheme+ hunkmod :: Monad m => FL FilePatchType wX wY -> B.ByteString -> m B.ByteString- hunkmod NilFL ps = return ps- hunkmod (Hunk line old new:>:hs) ps- = case applyHunkLines [(line,old,new)] ps of- Just ps' -> hunkmod hs ps'- Nothing -> fail $ "### Error applying:\n" ++- renderString Encode- (showHunk NewFormat f line old new) ++- "\n### to file " ++ fn2fp f ++ ":\n" ++ BC.unpack ps+ hunkmod NilFL content = return content+ hunkmod (Hunk line old new:>:hs) content =+ applyHunk f (line, old, new) content >>= hunkmod hs hunkmod _ _ = impossible- applyPrimFL (p:>:ps) = do apply p- applyPrimFL ps+ applyPrimFL (p:>:ps) = apply p >> applyPrimFL ps -applyHunks :: [(Int, [B.ByteString], [B.ByteString])]- -> B.ByteString -> Maybe [B.ByteString]-applyHunks [] ps = Just [ps]-applyHunks ((l, [], n):hs) ps- = case breakBeforeNthNewline (l - 2) ps of- (prfix, after_prefix) -> do rest <- applyHunks hs after_prefix- return $ intersperse nl (prfix:n) ++ rest- where nl = BC.singleton '\n'-applyHunks ((l, o, n):hs) ps- = case breakBeforeNthNewline (l - 2) ps of- (prfix, after_prefix) ->- case breakBeforeNthNewline (length o) after_prefix of- (oo, _) | oo /= unlinesPS (B.empty:o) -> fail "applyHunks error"- (_, suffix) ->- do rest <- applyHunks hs suffix- return $ intersperse nl (prfix:n) ++ rest- where nl = BC.singleton '\n'+applyHunk :: Monad m+ => FileName+ -> (Int, [B.ByteString], [B.ByteString])+ -> FileContents+ -> m FileContents+applyHunk f h fc =+ case applyHunkLines h fc of+ Right fc' -> return fc'+ Left msg ->+ fail $+ "### Error applying:\n" ++ renderHunk h +++ "\n### to file " ++ fn2fp f ++ ":\n" ++ BC.unpack fc +++ "### Reason: " ++ msg+ where+ renderHunk (l, o, n) = renderString (showHunk UserFormat f l o n) -applyHunkLines :: [(Int, [B.ByteString], [B.ByteString])]- -> FileContents -> Maybe FileContents-applyHunkLines [] c = Just c-applyHunkLines [(1, [], n)] ps | B.null ps = Just $ unlinesPS (n++[B.empty])-applyHunkLines hs@((l, o, n):hs') ps =- do pss <- case l of- 1 -> case breakAfterNthNewline (length o) ps of- Nothing -> if ps == unlinesPS o- then return $ intersperse nl n- else fail "applyHunkLines: Unexpected hunks"- Just (shouldbeo, suffix)- | shouldbeo /= unlinesPS (o++[B.empty]) ->- fail "applyHunkLines: Bad patch!"- | null n ->- do x <- applyHunkLines hs' suffix- return [x]- | otherwise ->- do rest <- applyHunks hs' suffix- return $ intersperse nl n ++ nl:rest- _ | l < 0 -> bug "Prim.applyHunkLines: After -ve lines?"- | otherwise -> applyHunks hs ps- let result = B.concat pss- return result- where nl = BC.singleton '\n'+{- The way darcs handles newlines is not easy to understand.++Everything seems pretty logical and conventional as long as files end in a+newline. In this case, the lines in a hunk can be regarded as newline+terminated, too. However, this view breaks down if we consider files that+are not newline terminated.++Here is a different view that covers the general case and explains,+conceptually, the algorithm below.++* Ever line (in a hunk or file) is regarded as being /preceded/ by a newline+ character.++* Every file starts out containing a single newline character, that is, a+ single empty line. A first empty line at the start of a file (if present)+ is /invisible/.++* When lines are appended to a file by a hunk, they are inserted /before/ a+ final empty line, if there is one. This results in a file that remains+ being terminated by a newline.++* In particular, when we start with an empty file and add a line, we push+ the invisible newline back, making it visible, and the newline that+ initiates our new content becomes invisible instead. This results in a+ newline terminated file, as above.++* However, if there is a newline at the end of a file (remember that this+ includes the case of an empty file), a hunk can /remove/ it by removing an+ empty line before adding anything. This results in a file that is /not/+ newline terminated.++The invisible newline character at the front is, of course, not present+anywhere in the representation of files, it is just a conceptual tool.++The algorithm below is highly optimized to minimize allocation of+intermediate ByteStrings. -}++applyHunkLines :: (Int, [B.ByteString], [B.ByteString])+ -> FileContents+ -> Either String FileContents+applyHunkLines (line, old, new) content+ | line == 1 =+ {- This case is subtle because here we have to deal with any invisible+ newline at the front of a file without it actually being present. We+ first try to drop everything up to the (length old)'th newline. ++ If this fails, we know that the content was not newline terminated. So+ we replace everything with the new content, interspersing but not+ terminating the lines with newline characters.++ If it succeeds, we insert the new content, interspersing /and/+ terminating the lines with newline characters before appending the+ rest of the content. -}+ case breakAfterNthNewline (length old) content of+ Nothing+ -- old content is not newline terminated+ | content == unlinesPS old -> Right $ unlinesPS new+ | otherwise -> Left "Hunk wants to remove content that isn't there"+ Just (should_be_old, suffix)+ -- old content is newline terminated+ | should_be_old == BC.unlines old ->+ Right $ unlinesPS $ new ++ [suffix]+ | otherwise ->+ Left "Hunk wants to remove content that isn't there"+ | line >= 2 = do+ {- This is the simpler case. We can be sure that we have at least one+ newline character at the point where we modify the file. This means we+ can apply the conceptual view literally, i.e. replace old content with+ new content /before/ this newline, where the lines in the old and new+ content are /preceded/ by newline characters. -}+ (pre, start) <- breakBeforeNthNewline (line-2) content+ let hunkContent ls = unlinesPS (B.empty:ls)+ post <- dropPrefix (hunkContent old) start+ return $ B.concat [pre, hunkContent new, post]+ | otherwise = Left "Hunk has zero or negative line number"+ where+ dropPrefix x y+ | x `B.isPrefixOf` y = Right $ B.drop (B.length x) y+ | otherwise =+ Left $ "Hunk wants to remove content that isn't there"++breakAfterNthNewline :: Int -> B.ByteString -> Maybe (B.ByteString, B.ByteString)+breakAfterNthNewline 0 the_ps = Just (B.empty, the_ps)+breakAfterNthNewline n _ | n < 0 = error "precondition of breakAfterNthNewline"+breakAfterNthNewline n the_ps = go n (B.elemIndices (BI.c2w '\n') the_ps)+ where+ go _ [] = Nothing -- we have fewer than n newlines+ go 1 (i:_) = Just $ B.splitAt (i + 1) the_ps+ go !m (_:is) = go (m - 1) is++breakBeforeNthNewline :: Int -> B.ByteString -> Either String (B.ByteString, B.ByteString)+breakBeforeNthNewline n _ | n < 0 = error "precondition of breakBeforeNthNewline"+breakBeforeNthNewline n the_ps = go n (B.elemIndices (BI.c2w '\n') the_ps)+ where+ go 0 [] = Right (the_ps, B.empty)+ go 0 (i:_) = Right $ B.splitAt i the_ps+ go !m (_:is) = go (m - 1) is+ go _ [] = Left "Line number does not exist"
@@ -1,5 +1,4 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP #-} module Darcs.Patch.Prim.V1.Coalesce () where@@ -23,7 +22,7 @@ , comparePrim, isIdentity ) import Darcs.Patch.Prim.V1.Show ()-import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..), EqCheck(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..), (:>)(..) , reverseRL, mapFL, mapFL_FL@@ -41,8 +40,6 @@ import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Path ( FileName, fp2fn ) -#include "impossible.h"- -- | '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.@@ -81,9 +78,11 @@ -data Simple wX wY = SFP !(FilePatchType wX wY) | SDP !(DirPatchType wX wY)- | SCP String String String- deriving ( Show )+data Simple wX wY+ = SFP !(FilePatchType wX wY)+ | SDP !(DirPatchType wX wY)+ | SCP String String String+ deriving ( Show ) toSimple :: Prim wX wY -> Maybe (FileName, Simple wX wY) toSimple (FP a b) = Just (a, SFP b)
@@ -1,10 +1,13 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP #-} module Darcs.Patch.Prim.V1.Commute ( Perhaps(..)- , subcommutes, WrappedCommuteFunction(..)- )- where+ , toPerhaps+ , CommuteFunction+ , speedyCommute+ , cleverCommute+ , commuteFiledir+ , commuteFilepatches+ ) where import Prelude () import Darcs.Prelude@@ -13,8 +16,8 @@ import Control.Monad ( MonadPlus, msum, mzero, mplus ) import Control.Applicative ( Alternative(..) ) -import qualified Data.ByteString as B (ByteString, concat)-import qualified Data.ByteString.Char8 as BC (pack)+import qualified Data.ByteString as B ( ByteString )+import qualified Data.ByteString.Char8 as BC ( pack ) import Darcs.Util.Path ( FileName, fn2fp, movedirfilename ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )@@ -24,9 +27,7 @@ import Darcs.Patch.Invert ( Invert(..) ) import Darcs.Patch.Commute ( Commute(..) ) import Darcs.Patch.Permutations () -- for Invert instance of FL-import Darcs.Patch.TokenReplace ( tryTokInternal )--#include "impossible.h"+import Darcs.Patch.TokenReplace ( tryTokReplace ) isInDirectory :: FileName -> FileName -> Bool isInDirectory d f = iid (fn2fp d) (fn2fp f)@@ -162,16 +163,7 @@ commuteFiledir _ = Unknown type CommuteFunction = forall wX wY . (Prim :> Prim) wX wY -> Perhaps ((Prim :> Prim) wX wY)-newtype WrappedCommuteFunction = WrappedCommuteFunction { runWrappedCommuteFunction :: CommuteFunction } -subcommutes :: [(String, WrappedCommuteFunction)]-subcommutes =- [("speedyCommute", WrappedCommuteFunction speedyCommute),- ("commuteFiledir", WrappedCommuteFunction (cleverCommute commuteFiledir)),- ("commuteFilepatches", WrappedCommuteFunction (cleverCommute commuteFilepatches)),- ("commutex", WrappedCommuteFunction (toPerhaps . commute))- ]- commuteFilepatches :: CommuteFunction commuteFilepatches (FP f1 p1 :> FP f2 p2) | f1 == f2 = commuteFP f1 (p1 :> p2) commuteFilepatches _ = Unknown@@ -179,21 +171,25 @@ 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))+ 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+ Succeeded (FP f (unsafeCoerceP p2) :> FP f (Hunk line1 [] []))+commuteFP f (Hunk line1 old1 new1 :> Hunk line2 old2 new2) =+ case commuteHunkLines line1 (length old1) (length new1) line2 (length old2) (length new2) of+ Just (line2', line1') ->+ Succeeded (FP f (Hunk line2' old2 new2) :> FP f (Hunk line1' old1 new1))+ Nothing -> Failed+commuteFP f (Hunk line1 old1 new1 :> TokReplace t o n) =+ let po = BC.pack o; pn = BC.pack n in+ case tryTokReplaces t po pn old1 of Nothing -> Failed Just old1' ->- case tryTokReplace t o n new1 of+ case tryTokReplaces t po pn 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+ | t1 /= t2 = Failed | o1 == o2 = Failed | n1 == o2 = Failed | o1 == n2 = Failed@@ -202,31 +198,23 @@ FP f (TokReplace t1 o1 n1)) commuteFP _ _ = Unknown -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 (line2 - lengthnew1 + lengthold1) old2 new2) :>- FP f (Hunk line1 old1 new1))- | line2 + lengthold2 < line1 =- 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 (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 line2 old2 new2) :>- FP f (Hunk (line1 + lengthnew2 - lengthold2) old1 new1))- | otherwise = seq f Nothing- where lengthnew1 = length new1- lengthnew2 = length new2- lengthold1 = length old1- lengthold2 = length old2-commuteHunk _ _ = impossible+commuteHunkLines :: Int -> Int -> Int -> Int -> Int -> Int+ -> Maybe (Int, Int)+commuteHunkLines line1 len_old1 len_new1 line2 len_old2 len_new2+ | line1 + len_new1 < line2 = Just (line2 - len_new1 + len_old1, line1)+ | line2 + len_old2 < line1 = Just (line2, line1 + len_new2 - len_old2)+ | len_old2 /= 0+ , len_old1 /= 0+ , len_new2 /= 0+ , len_new1 /= 0+ , line1 + len_new1 == line2 = Just (line2 - len_new1 + len_old1, line1)+ | len_old2 /= 0+ , len_old1 /= 0+ , len_new2 /= 0+ , len_new1 /= 0+ , line2 + len_old2 == line1 = Just (line2, line1 + len_new2 - len_old2)+ | otherwise = Nothing -tryTokReplace :: String -> String -> String- -> [B.ByteString] -> Maybe [B.ByteString]-tryTokReplace t o n =- mapM (fmap B.concat . tryTokInternal t (BC.pack o) (BC.pack n))+tryTokReplaces :: String -> B.ByteString -> B.ByteString+ -> [B.ByteString] -> Maybe [B.ByteString]+tryTokReplaces t o n = mapM (tryTokReplace t o n)
@@ -15,9 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}-- module Darcs.Patch.Prim.V1.Core ( Prim(..), DirPatchType(..), FilePatchType(..),@@ -29,12 +26,10 @@ import Prelude () import Darcs.Prelude -import Prelude hiding ( pi )- import qualified Data.ByteString as B (ByteString) import Darcs.Util.Path ( FileName, fn2fp, fp2fn, normPath )-import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..), EqCheck(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) import Darcs.Patch.Debug ( PatchDebug(..) ) import Darcs.Patch.FileHunk ( FileHunk(..), IsHunk(..) )@@ -49,19 +44,21 @@ FP :: !FileName -> !(FilePatchType wX wY) -> Prim wX wY ChangePref :: !String -> !String -> !String -> Prim wX wY -data FilePatchType wX wY = RmFile | AddFile- | Hunk !Int [B.ByteString] [B.ByteString]- | TokReplace !String !String !String- | Binary B.ByteString B.ByteString- deriving (Eq,Ord)+data FilePatchType wX wY+ = RmFile+ | AddFile+ | Hunk !Int [B.ByteString] [B.ByteString]+ | TokReplace !String !String !String+ | Binary B.ByteString B.ByteString+ deriving (Eq,Ord) data DirPatchType wX wY = RmDir | AddDir deriving (Eq,Ord) -instance MyEq FilePatchType where+instance Eq2 FilePatchType where unsafeCompare a b = a == unsafeCoerceP b -instance MyEq DirPatchType where+instance Eq2 DirPatchType where unsafeCompare a b = a == unsafeCoerceP b isIdentity :: Prim wX wY -> EqCheck wX wY@@ -153,7 +150,7 @@ instance PatchDebug Prim -instance MyEq Prim where+instance Eq2 Prim where unsafeCompare (Move a b) (Move c d) = a == c && b == d unsafeCompare (DP d1 p1) (DP d2 p2) = d1 == d2 && p1 `unsafeCompare` p2
@@ -6,17 +6,19 @@ import Darcs.Patch.Prim.Class ( PrimRead(..), hunk, binary ) import Darcs.Patch.Prim.V1.Core- ( Prim(..),- DirPatchType(..), FilePatchType(..) )+ ( Prim(..)+ , DirPatchType(..)+ , FilePatchType(..)+ ) import Darcs.Util.Path ( fn2fp )-import Darcs.Patch.Format ( FileNameFormat(..) )-import Darcs.Patch.Read ( ReadPatch(..), readFileName )-import Darcs.Patch.ReadMonads (ParserM, takeTillChar,- string, int,- option, choice,- anyChar, char, myLex',- skipSpace, skipWhile, linesStartingWith)+import Darcs.Patch.Format ( FileNameFormat )+import Darcs.Patch.Read ( readFileName )+import Darcs.Patch.ReadMonads+ ( ParserM, takeTillChar, string, int+ , option, choice, anyChar, char, myLex'+ , skipSpace, skipWhile, linesStartingWith+ ) import Darcs.Patch.Witnesses.Sealed ( seal ) @@ -27,20 +29,17 @@ import qualified Data.ByteString.Char8 as BC ( unpack, pack ) -instance ReadPatch Prim where- readPatch' = readPrim OldFormat- instance PrimRead Prim where- readPrim x+ readPrim fmt = skipSpace >> choice- [ return' $ readHunk x- , return' $ readAddFile x- , return' $ readAddDir x- , return' $ readMove x- , return' $ readRmFile x- , return' $ readRmDir x- , return' $ readTok x- , return' $ readBinary x+ [ return' $ readHunk fmt+ , return' $ readAddFile fmt+ , return' $ readAddDir fmt+ , return' $ readMove fmt+ , return' $ readRmFile fmt+ , return' $ readRmDir fmt+ , return' $ readTok fmt+ , return' $ readBinary fmt , return' readChangePref ] where@@ -74,30 +73,31 @@ changepref = BC.pack "changepref" readHunk :: ParserM m => FileNameFormat -> m (Prim wX wY)-readHunk x = do+readHunk fmt = do string hunk' fi <- myLex' l <- int have_nl <- skipNewline if have_nl- then do _ <- linesStartingWith ' ' -- skipping context- old <- linesStartingWith '-'- new <- linesStartingWith '+'- _ <- linesStartingWith ' ' -- skipping context- return $ hunk (fn2fp $ readFileName x fi) l old new- else return $ hunk (fn2fp $ readFileName x fi) l [] []+ then do+ _ <- linesStartingWith ' ' -- skipping context+ old <- linesStartingWith '-'+ new <- linesStartingWith '+'+ _ <- linesStartingWith ' ' -- skipping context+ return $ hunk (fn2fp $ readFileName fmt fi) l old new+ else return $ hunk (fn2fp $ readFileName fmt fi) l [] [] skipNewline :: ParserM m => m Bool skipNewline = option False (char '\n' >> return True) readTok :: ParserM m => FileNameFormat -> m (Prim wX wY)-readTok x = do+readTok fmt = do string replace f <- myLex' regstr <- myLex' o <- myLex' n <- myLex'- return $ FP (readFileName x f) $ TokReplace (BC.unpack (drop_brackets regstr))+ return $ FP (readFileName fmt f) $ TokReplace (BC.unpack (drop_brackets regstr)) (BC.unpack o) (BC.unpack n) where drop_brackets = B.init . B.tail @@ -114,7 +114,7 @@ -- > *HEXHEXHEX -- > ... readBinary :: ParserM m => FileNameFormat -> m (Prim wX wY)-readBinary x = do+readBinary fmt = do string binary' fi <- myLex' _ <- myLex'@@ -123,44 +123,48 @@ _ <- myLex' skipSpace new <- linesStartingWith '*'- return $ binary (fn2fp $ readFileName x fi)+ return $ binary (fn2fp $ readFileName fmt fi) (fromHex2PS $ B.concat old) (fromHex2PS $ B.concat new) readAddFile :: ParserM m => FileNameFormat -> m (Prim wX wY)-readAddFile x = do string addfile- f <- myLex'- return $ FP (readFileName x f) AddFile+readAddFile fmt = do+ string addfile+ f <- myLex'+ return $ FP (readFileName fmt f) AddFile readRmFile :: ParserM m => FileNameFormat -> m (Prim wX wY)-readRmFile x = do string rmfile- f <- myLex'- return $ FP (readFileName x f) RmFile+readRmFile fmt = do+ string rmfile+ f <- myLex'+ return $ FP (readFileName fmt f) RmFile readMove :: ParserM m => FileNameFormat -> m (Prim wX wY)-readMove x = do string move- d <- myLex'- d' <- myLex'- return $ Move (readFileName x d) (readFileName x d')+readMove fmt = do+ string move+ d <- myLex'+ d' <- myLex'+ return $ Move (readFileName fmt d) (readFileName fmt d') readChangePref :: ParserM m => m (Prim wX wY)-readChangePref- = do string changepref- p <- myLex'- skipWhile (== ' ')- _ <- anyChar -- skip newline- f <- takeTillChar '\n'- _ <- anyChar -- skip newline- t <- takeTillChar '\n'- return $ ChangePref (BC.unpack p) (BC.unpack f) (BC.unpack t)+readChangePref = do+ string changepref+ p <- myLex'+ skipWhile (== ' ')+ _ <- anyChar -- skip newline+ f <- takeTillChar '\n'+ _ <- anyChar -- skip newline+ t <- takeTillChar '\n'+ return $ ChangePref (BC.unpack p) (BC.unpack f) (BC.unpack t) readAddDir :: ParserM m => FileNameFormat -> m (Prim wX wY)-readAddDir x = do string adddir- f <- myLex'- return $ DP (readFileName x f) AddDir+readAddDir fmt = do+ string adddir+ f <- myLex'+ return $ DP (readFileName fmt f) AddDir readRmDir :: ParserM m => FileNameFormat -> m (Prim wX wY)-readRmDir x = do string rmdir- f <- myLex'- return $ DP (readFileName x f) RmDir-+readRmDir fmt = do+ string rmdir+ f <- myLex'+ return $ DP (readFileName fmt f) RmDir
@@ -7,47 +7,31 @@ 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 Darcs.Util.Tree( Tree )--import Darcs.Patch.FileHunk ( FileHunk(..), IsHunk(..), showFileHunk )-import Darcs.Util.Path ( FileName )-import Darcs.Patch.Format ( PatchListFormat, FileNameFormat(..) )-import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..), formatFileName )-import Darcs.Patch.Apply( ApplyState )-import Darcs.Patch.Summary ( plainSummaryPrim, plainSummaryPrims )-import Darcs.Patch.Viewing ( showContextHunk )+import Darcs.Patch.Apply ( ApplyState )+import Darcs.Patch.FileHunk ( FileHunk(..), showFileHunk )+import Darcs.Patch.Format ( FileNameFormat )+import Darcs.Patch.Show ( formatFileName ) import Darcs.Patch.Permutations () -- for Invert instance of FL import Darcs.Patch.Prim.Class ( PrimShow(..) ) import Darcs.Patch.Prim.V1.Core ( Prim(..), FilePatchType(..), DirPatchType(..) ) import Darcs.Patch.Prim.V1.Details ()+import Darcs.Patch.Viewing ( showContextHunk ) import Darcs.Patch.Witnesses.Show ( Show1(..), Show2(..), ShowDict(..) )++import Darcs.Util.Path ( FileName ) import Darcs.Util.Printer ( Doc, vcat, text, userchunk, invisibleText, invisiblePS, blueText, ($$), (<+>), (<>), ) import Darcs.Util.Show ( appPrec, BSWrapper(..) )+import Darcs.Util.Tree ( Tree ) --- 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 (ApplyState Prim ~ Tree) => ShowPatch Prim where- showContextPatch (isHunk -> Just fh) = showContextHunk fh- showContextPatch p = return $ showPatch p- summary = plainSummaryPrim- summaryFL = plainSummaryPrims False []- thing _ = "change"- instance Show (Prim wX wY) where showsPrec d (Move fn1 fn2) = showParen (d > appPrec) $ showString "Move " . showsPrec (appPrec + 1) fn1 . showString " " .@@ -96,25 +80,27 @@ showsPrec _ RmDir = showString "RmDir" showsPrec _ AddDir = showString "AddDir" -instance PrimShow Prim where- showPrim x (FP f AddFile) = showAddFile x f- showPrim x (FP f RmFile) = showRmFile x f- showPrim x (FP f (Hunk line old new)) = showHunk x f line old new- showPrim x (FP f (TokReplace t old new)) = showTok x f t old new- showPrim x (FP f (Binary old new)) = showBinary x f old new- showPrim x (DP d AddDir) = showAddDir x d- showPrim x (DP d RmDir) = showRmDir x d- showPrim x (Move f f') = showMove x f f'+instance ApplyState Prim ~ Tree => PrimShow Prim where+ showPrim fmt (FP f AddFile) = showAddFile fmt f+ showPrim fmt (FP f RmFile) = showRmFile fmt f+ showPrim fmt (FP f (Hunk line old new)) = showHunk fmt f line old new+ showPrim fmt (FP f (TokReplace t old new)) = showTok fmt f t old new+ showPrim fmt (FP f (Binary old new)) = showBinary fmt f old new+ showPrim fmt (DP d AddDir) = showAddDir fmt d+ showPrim fmt (DP d RmDir) = showRmDir fmt d+ showPrim fmt (Move f f') = showMove fmt f f' showPrim _ (ChangePref p f t) = showChangePref p f t+ showPrimCtx fmt (FP f (Hunk line old new)) = showContextHunk fmt (FileHunk f line old new)+ showPrimCtx fmt p = return $ showPrim fmt p showAddFile :: FileNameFormat -> FileName -> Doc-showAddFile x f = blueText "addfile" <+> formatFileName x f+showAddFile fmt f = blueText "addfile" <+> formatFileName fmt f showRmFile :: FileNameFormat -> FileName -> Doc-showRmFile x f = blueText "rmfile" <+> formatFileName x f+showRmFile fmt f = blueText "rmfile" <+> formatFileName fmt f showMove :: FileNameFormat -> FileName -> FileName -> Doc-showMove x d d' = blueText "move" <+> formatFileName x d <+> formatFileName x d'+showMove fmt d d' = blueText "move" <+> formatFileName fmt d <+> formatFileName fmt d' showChangePref :: String -> String -> String -> Doc showChangePref p f t = blueText "changepref" <+> text p@@ -122,23 +108,23 @@ $$ userchunk t showAddDir :: FileNameFormat -> FileName -> Doc-showAddDir x d = blueText "adddir" <+> formatFileName x d+showAddDir fmt d = blueText "adddir" <+> formatFileName fmt d showRmDir :: FileNameFormat -> FileName -> Doc-showRmDir x d = blueText "rmdir" <+> formatFileName x d+showRmDir fmt d = blueText "rmdir" <+> formatFileName fmt d showHunk :: FileNameFormat -> FileName -> Int -> [B.ByteString] -> [B.ByteString] -> Doc-showHunk x f line old new = showFileHunk x (FileHunk f line old new)+showHunk fmt f line old new = showFileHunk fmt (FileHunk f line old new) showTok :: FileNameFormat -> FileName -> String -> String -> String -> Doc-showTok x f t o n = blueText "replace" <+> formatFileName x f+showTok fmt f t o n = blueText "replace" <+> formatFileName fmt f <+> text "[" <> userchunk t <> text "]" <+> userchunk o <+> userchunk n showBinary :: FileNameFormat -> FileName -> B.ByteString -> B.ByteString -> Doc-showBinary x f o n =- blueText "binary" <+> formatFileName x f+showBinary fmt f o n =+ blueText "binary" <+> formatFileName fmt f $$ invisibleText "oldhex" $$ vcat (map makeprintable $ breakEvery 78 $ fromPS2Hex o) $$ invisibleText "newhex"
@@ -1,5 +1,3 @@-{-# LANGUAGE CPP, GADTs #-}- module Darcs.Patch.Progress ( progressRL , progressFL
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.Patch.Read ( ReadPatch(..), readPatch, readPatchPartial, bracketedFL, peekfor,@@ -26,11 +24,11 @@ import Prelude () import Darcs.Prelude -import Darcs.Util.ByteString ( dropSpace )+import Darcs.Util.ByteString ( dropSpace, unpackPSFromUTF8, decodeLocale ) import qualified Data.ByteString as B (ByteString, null) import Darcs.Patch.Bracketed ( Bracketed(..), unBracketedFL )-import Darcs.Util.Path ( FileName, fp2fn, ps2fn, decodeWhite )+import Darcs.Util.Path ( FileName, fp2fn, decodeWhite ) import Darcs.Patch.Format ( PatchListFormat(..), ListFormat(..), FileNameFormat(..) ) import Darcs.Patch.ReadMonads (ParserM, parseStrictly,@@ -42,9 +40,9 @@ import Control.Applicative ( (<|>) ) import Control.Monad ( mzero )-import qualified Data.ByteString.Char8 as BC ( ByteString, unpack )-+import qualified Data.ByteString.Char8 as BC ( ByteString, pack ) +-- | This class is used to decode patches from their binary representation. class ReadPatch p where readPatch' :: ParserM m => m (Sealed (p wX))@@ -116,6 +114,8 @@ , ifnot ] {-# INLINE peekfor #-} +-- See also Darcs.Patch.Show.formatFileName. readFileName :: FileNameFormat -> B.ByteString -> FileName-readFileName OldFormat = ps2fn-readFileName NewFormat = fp2fn . decodeWhite . BC.unpack+readFileName OldFormat = fp2fn . decodeWhite . decodeLocale . BC.pack . unpackPSFromUTF8+readFileName NewFormat = fp2fn . decodeWhite . decodeLocale+readFileName UserFormat = error "readFileName called with UserFormat"
@@ -6,6 +6,7 @@ , takeHeadRebaseFL , takeAnyRebase , takeAnyRebaseAndTrailingPatches+ , dropAnyRebase ) where import Prelude ()@@ -14,7 +15,13 @@ import Darcs.Patch.Named.Wrapped ( WrappedNamed(RebaseP) ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, hopefully ) import Darcs.Patch.Rebase.Container ( Suspended(..) )-import Darcs.Patch.RepoType ( RepoType(..), RebaseType(..) )+import Darcs.Patch.RepoType+ ( RepoType(..)+ , RebaseType(..)+ , IsRepoType(..)+ , SRepoType(..)+ , SRebaseType(..)+ ) import Darcs.Patch.Set ( PatchSet(..) ) import Darcs.Patch.Witnesses.Ordered import Darcs.Patch.Witnesses.Sealed@@ -77,56 +84,76 @@ -} --- |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.+-- | 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 ('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"+ bug "internal error: no suspended patch found" 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.+-- | Given the repository contents, get the rebase container patch, its+-- contents, and the rest of the repository contents. The rebase patch can be+-- anywhere in the repository and is returned without being commuted to the end. takeAnyRebaseAndTrailingPatches :: PatchSet ('RepoType 'IsRebase) p wA wB- -> FlippedSeal (PatchInfoAnd ('RepoType 'IsRebase) p :> RL (PatchInfoAnd ('RepoType 'IsRebase) p)) 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"+ bug "internal error: no suspended patch found" 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.+-- | Remove the rebase patch from a 'PatchSet'.+dropAnyRebase :: forall rt p wA wB. IsRepoType rt+ => PatchSet rt p wA wB -> PatchSet rt p wA wB+dropAnyRebase ps@(PatchSet tags patches) =+ case singletonRepoType::SRepoType rt of+ SRepoType SNoRebase -> ps+ SRepoType SIsRebase -> PatchSet tags (dropRebaseRL patches)++-- | Remove the rebase patch from an 'RL' of patches.+dropRebaseRL :: RL (PatchInfoAnd ('RepoType 'IsRebase) p) wA wB+ -> RL (PatchInfoAnd ('RepoType 'IsRebase) p) wA wB+dropRebaseRL NilRL = bug "internal error: no suspended patch found"+dropRebaseRL (ps :<: p)+ | RebaseP _ _ <- hopefully p = ps+ | otherwise = dropRebaseRL 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 ('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"+takeHeadRebase _ =+ bug "internal error: must have a rebase container patch at end of repository" +-- | Same as 'takeHeadRebase' but for an 'RL' of patches. 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 (ps :<: p) | RebaseP _ rs <- hopefully p = (p, rs, ps)- | otherwise = error "internal error: must have a suspended patch at end of repository"+takeHeadRebaseRL _ =+ bug "internal error: must have a suspended patch at end of repository" +-- | Same as 'takeHeadRebase' but for an 'FL' of patches. 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)-+takeHeadRebaseFL ps =+ let (a, b, c) = takeHeadRebaseRL (reverseFL ps) in (a, b, reverseRL c)
@@ -14,11 +14,12 @@ 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.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Inspect ( PatchInspect(..) )+import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.Show ( ShowPatch(..) )+import Darcs.Patch.Prim ( PrimPatchBase(..), FromPrim(..), FromPrim(..) ) import Darcs.Patch.Read ( bracketedFL ) import Darcs.Patch.Rebase.Fixup ( RebaseFixup(..), namedToFixups ) import Darcs.Patch.Rebase.Item ( RebaseItem(..) )@@ -91,24 +92,18 @@ resolveConflicts _ = [] conflictedEffect _ = [] -instance Apply p => Apply (Suspended p) where+instance 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)+ showPatch f (Items ps) = blueText "rebase" <+> text "0.0" <+> blueText "{"- $$ vcat (mapFL showPatch ps)+ $$ vcat (mapFL (showPatch f) 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@@ -129,13 +124,13 @@ [] -> Nothing xs -> Just (vcat xs) -instance Apply p => Repair (Suspended p) where+instance 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+instance RepairToFL (Suspended p) where applyAndTryToFixFL s = fmap (second $ (:>: NilFL)) <$> applyAndTryToFix s -- Just repair the internals of the patch, without applying it to anything
@@ -20,13 +20,13 @@ import Darcs.Patch.Inspect ( PatchInspect(..) ) import Darcs.Patch.Invert ( Invert(..) ) import Darcs.Patch.Named ( Named(..), commuterNamedId, commuterIdNamed )-import Darcs.Patch.Prim ( PrimPatchBase, PrimOf )+import Darcs.Patch.Prim ( PrimPatchBase(..), PrimPatch ) import Darcs.Patch.Rebase.Name ( RebaseName(..) , commuteNamedName, commuteNameNamed , commutePrimName, commuteNamePrim )-import Darcs.Patch.Witnesses.Eq ( MyEq(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL_FL, (:>)(..), (+>+) ) import Darcs.Patch.Witnesses.Show@@ -37,10 +37,10 @@ -- |A single rebase fixup, needed to ensure that the actual patches -- being stored in the rebase state have the correct context. data RebaseFixup p wX wY where- PrimFixup :: PrimOf p wX wY -> RebaseFixup p wX wY+ PrimFixup :: PrimPatch (PrimOf p) => 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 :: (PrimPatch (PrimOf p), 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@@ -58,7 +58,7 @@ instance PrimPatchBase p => PrimPatchBase (RebaseFixup p) where type PrimOf (RebaseFixup p) = PrimOf p -instance (PrimPatchBase p, Apply p, ApplyState p ~ ApplyState (PrimOf p)) => Apply (RebaseFixup p) where+instance (PrimPatchBase p, ApplyState p ~ ApplyState (PrimOf p)) => Apply (RebaseFixup p) where type ApplyState (RebaseFixup p) = ApplyState p apply (PrimFixup p) = apply p apply (NameFixup p) = apply p@@ -67,7 +67,7 @@ effect (PrimFixup p) = p :>: NilFL effect (NameFixup p) = effect p -instance MyEq (PrimOf p) => MyEq (RebaseFixup p) where+instance Eq2 (PrimOf p) => Eq2 (RebaseFixup p) where PrimFixup p1 `unsafeCompare` PrimFixup p2 = p1 `unsafeCompare` p2 PrimFixup _ `unsafeCompare` _ = False _ `unsafeCompare` PrimFixup _ = False
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, UndecidableInstances #-}+{-# LANGUAGE UndecidableInstances #-} module Darcs.Patch.Rebase.Item ( RebaseItem(..) , simplifyPush, simplifyPushes@@ -14,11 +14,14 @@ 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.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Inspect ( PatchInspect(..) )+import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.Show ( ShowPatch(..) )+import Darcs.Patch.Prim+ ( PrimPatchBase, PrimOf, FromPrim(..), FromPrim(..), canonizeFL ) import Darcs.Patch.Rebase.Fixup ( RebaseFixup(..) ) import Darcs.Patch.Rebase.Name ( RebaseName(..)@@ -27,6 +30,7 @@ ) import Darcs.Patch.Repair ( Check(..) ) import Darcs.Patch.Show ( ShowPatchBasic(..) )+import Darcs.Patch.Summary ( plainSummaryPrim ) import Darcs.Patch.ReadMonads ( ParserM, lexString ) import Darcs.Patch.Witnesses.Eq import Darcs.Patch.Witnesses.Ordered@@ -43,8 +47,6 @@ 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@@ -145,17 +147,16 @@ 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 ")"+ showPatch f (ToEdit p) = blueText "rebase-toedit" <+> blueText "(" $$ showPatch f p $$ blueText ")"+ showPatch f (Fixup (PrimFixup p)) = blueText "rebase-fixup" <+> blueText "(" $$ showPatch f p $$ blueText ")" where+ showPatch f (Fixup (NameFixup p)) = blueText "rebase-name" <+> blueText "(" $$ showPatch f 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 (PrimFixup p)) = plainSummaryPrim p summary (Fixup (NameFixup n)) = summary n summaryFL ps = vcat (mapFL summary ps) -- TODO sort out summaries properly, considering expected conflicts
@@ -2,7 +2,6 @@ -- -- BSD3 -{-# LANGUAGE CPP #-} module Darcs.Patch.Rebase.Name ( RebaseName(..) , commuteNamePrim, commutePrimName@@ -18,15 +17,16 @@ import Darcs.Patch.Info ( PatchInfo, isInverted, showPatchInfo, readPatchInfo ) import Darcs.Patch.Inspect ( PatchInspect(..) ) import Darcs.Patch.Named ( Named(..) )-import Darcs.Patch.Patchy- ( Invert(..), Commute(..), Patchy, Apply(..)- , ShowPatch(..), ReadPatch(..)- )+import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.Show ( ShowPatch(..) ) import Darcs.Patch.Permutations ( inverseCommuter )-import Darcs.Patch.Prim ( PrimPatchBase, PrimOf )+import Darcs.Patch.Prim ( PrimPatchBase(..) ) import Darcs.Patch.ReadMonads ( lexString ) import Darcs.Patch.Show ( ShowPatchBasic(..) )-import Darcs.Patch.Witnesses.Eq ( MyEq(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..) ) import Darcs.Patch.Witnesses.Ordered ( (:>)(..), FL(..) ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..) ) import Darcs.Patch.Witnesses.Show@@ -40,8 +40,6 @@ import Control.Applicative ( (<|>) ) import qualified Data.ByteString.Char8 as BC ( pack ) -#include "impossible.h"- -- Note: in principle this is a general concept not limited to -- rebase, and we might be able to generalise this type and -- refactor named patches to use it too.@@ -63,9 +61,9 @@ showDict2 = ShowDictClass instance ShowPatchBasic (RebaseName p) where- showPatch (AddName n) = blueText "addname" $$ showPatchInfo n- showPatch (DelName n) = blueText "delname" $$ showPatchInfo n- showPatch (Rename old new) = blueText "rename" $$ showPatchInfo old $$ showPatchInfo new+ showPatch f (AddName n) = blueText "addname" $$ showPatchInfo f n+ showPatch f (DelName n) = blueText "delname" $$ showPatchInfo f n+ showPatch f (Rename old new) = blueText "rename" $$ showPatchInfo f old $$ showPatchInfo f new instance ShowPatch (RebaseName p) where summary _ = empty -- TODO improve this?@@ -130,19 +128,17 @@ listTouchedFiles _ = [] hunkMatches _ _ = False -instance Apply p => Apply (RebaseName p) where+instance Apply (RebaseName p) where type ApplyState (RebaseName p) = ApplyState p apply _ = return () -instance Apply p => Patchy (RebaseName p)- instance PrimPatchBase p => PrimPatchBase (RebaseName p) where type PrimOf (RebaseName p) = PrimOf p instance Effect (RebaseName p) where effect _ = unsafeCoerceP NilFL -instance MyEq (RebaseName p) where+instance Eq2 (RebaseName p) where AddName n1 `unsafeCompare` AddName n2 = n1 == n2 AddName _ `unsafeCompare` _ = False _ `unsafeCompare` AddName _ = False@@ -158,12 +154,12 @@ -- |Commute a name patch and a primitive patch. They trivially -- commute so this just involves changing the witnesses.-commuteNamePrim :: PrimPatchBase p => (RebaseName p :> PrimOf p) wX wY -> (PrimOf p :> RebaseName p) wX wY+commuteNamePrim :: (RebaseName p :> PrimOf p) wX wY -> (PrimOf p :> RebaseName p) wX wY commuteNamePrim (n :> f) = unsafeCoerceP f :> unsafeCoerceP n -- |Commute a primitive patch and a name patch. They trivially -- commute so this just involves changing the witnesses.-commutePrimName :: PrimPatchBase p => (PrimOf p :> RebaseName p) wX wY -> (RebaseName p :> PrimOf p) wX wY+commutePrimName :: (PrimOf p :> RebaseName p) wX wY -> (RebaseName p :> PrimOf p) wX wY commutePrimName (f :> n) = unsafeCoerceP n :> unsafeCoerceP f -- |Commute a name patch and a named patch. In most cases this is
@@ -1,7 +1,7 @@ -- Copyright (C) 2009 Ganesh Sittampalam -- -- BSD3-{-# LANGUAGE CPP, GADTs, UndecidableInstances #-}+{-# LANGUAGE UndecidableInstances #-} module Darcs.Patch.Rebase.Viewing ( RebaseSelect(..) , toRebaseSelect, fromRebaseSelect, extractRebaseSelect, reifyRebaseSelect@@ -44,12 +44,14 @@ ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia )-import Darcs.Patch.Patchy ( Invert(..), Commute(..), Patchy, Apply(..),- ShowPatch(..), ReadPatch(..),- PatchInspect(..)- )+import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Inspect ( PatchInspect(..) )+import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.Show ( ShowPatch(..) ) import Darcs.Patch.Prim- ( PrimPatch, PrimPatchBase, PrimOf, FromPrim(..), FromPrims(..)+ ( PrimPatch, PrimPatchBase(..), FromPrim(..), FromPrims(..) ) import Darcs.Patch.Rebase.Container ( Suspended(..) ) import Darcs.Patch.Rebase.Fixup@@ -60,7 +62,7 @@ import Darcs.Patch.Rebase.Item ( RebaseItem(..) ) import Darcs.Patch.Rebase.Name ( RebaseName(..) ) import Darcs.Patch.RepoType ( RepoType(..), RebaseType(..) )-import Darcs.Patch.Show ( ShowPatchBasic(..) )+import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatchFor(..), ShowContextPatch(..) ) import Darcs.Patch.Summary ( plainSummary ) import Darcs.Patch.Witnesses.Eq import Darcs.Patch.Witnesses.Ordered@@ -77,8 +79,6 @@ import Data.List ( nub, (\\) ) import Data.Maybe ( fromMaybe ) -#include "impossible.h"- -- |Encapsulate a single patch in the rebase state together with its fixups. -- Used during interactive selection to make sure that each item presented -- to the user corresponds to a patch.@@ -142,14 +142,6 @@ 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))- => Patchy (RebaseSelect p)--instance ( PrimPatchBase p, Apply p, ApplyState p ~ ApplyState (PrimOf p)- , Invert p- )- => Patchy (RebaseChange p)- instance PatchDebug p => PatchDebug (RebaseSelect p) instance PatchDebug p => PatchDebug (RebaseChange p)@@ -168,7 +160,7 @@ apply (RCFwd fixups contents) = apply fixups >> apply contents apply (RCRev fixups contents) = apply (invert contents) >> apply (invertFL fixups) -instance (PrimPatchBase p, FromPrim p, Conflict p, CommuteNoConflicts p, Invert p) => Conflict (RebaseSelect p) where+instance (PrimPatchBase p, Conflict p, CommuteNoConflicts p, Invert p) => Conflict (RebaseSelect p) where resolveConflicts (RSFwd _ toedit) = resolveConflicts toedit resolveConflicts (RSRev{}) = impossible @@ -227,14 +219,19 @@ invertRL . reverseFL . effect $ RCFwd fixups changes instance (PrimPatchBase p, PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (RebaseSelect p) where- showPatch (RSFwd fixups toedit) =- showPatch (Items (mapFL_FL Fixup fixups +>+ ToEdit toedit :>: NilFL))- showPatch (RSRev {}) = impossible+ showPatch f (RSFwd fixups toedit) =+ showPatch f (Items (mapFL_FL Fixup fixups +>+ ToEdit toedit :>: NilFL))+ showPatch _ (RSRev {}) = impossible -instance (PrimPatchBase p, PatchListFormat p, ShowPatchBasic p)+-- TODO this is a dummy instance that does not actually show context+instance (PrimPatchBase p, PatchListFormat p, ShowPatchBasic p) => ShowContextPatch (RebaseSelect p) where+ showContextPatch f p = return $ showPatch f p++instance (PrimPatchBase p, ShowPatchBasic p) => ShowPatchBasic (RebaseChange p) where- showPatch (RCFwd fixups contents) =- vcat (mapFL showPatch contents) $$+ showPatch ForStorage _ = impossible+ showPatch ForDisplay (RCFwd fixups contents) =+ vcat (mapFL (showPatch ForDisplay) contents) $$ (if nullFL fixups then empty else@@ -244,9 +241,9 @@ vcat (mapRL showFixup (invertFL fixups)) ) where- showFixup (PrimFixup p) = showPatch p- showFixup (NameFixup n) = showPatch n- showPatch (RCRev {}) = impossible+ showFixup (PrimFixup p) = showPatch ForDisplay p+ showFixup (NameFixup n) = showPatch ForDisplay n+ showPatch _ (RCRev {}) = impossible instance (PrimPatchBase p, PatchListFormat p, Apply p, CommuteNoConflicts p, Conflict p, IsHunk p, ShowPatch p) => ShowPatch (RebaseSelect p) where@@ -267,8 +264,14 @@ summary = plainSummary summaryFL = plainSummary +-- TODO this is a dummy instance that does not actually show context+instance+ ( PrimPatchBase p, ShowPatchBasic p)+ => ShowContextPatch (RebaseChange p) where -instance ReadPatch p => ReadPatch (RebaseSelect p) where+ showContextPatch f p = return $ showPatch f p++instance ReadPatch (RebaseSelect p) where readPatch' = error "can't read RebaseSelect patches" instance ReadPatch (RebaseChange p) where@@ -320,7 +323,7 @@ type PrimOf (RebaseChange p) = PrimOf p -instance Invert p => Invert (RebaseSelect p) where+instance Invert (RebaseSelect p) where invert (RSFwd fixups edit) = RSRev fixups edit invert (RSRev fixups edit) = RSFwd fixups edit @@ -328,7 +331,7 @@ invert (RCFwd fixups contents) = RCRev fixups contents invert (RCRev fixups contents) = RCFwd fixups contents -instance (PrimPatchBase p, Commute p, MyEq p) => MyEq (RebaseSelect p) where+instance (PrimPatchBase p, Commute p, Eq2 p) => Eq2 (RebaseSelect p) where RSFwd fixups1 edit1 =\/= RSFwd fixups2 edit2 | IsEq <- fixups1 =\/= fixups2, IsEq <- edit1 =\/= edit2 = IsEq RSRev fixups1 edit1 =\/= RSRev fixups2 edit2@@ -460,7 +463,7 @@ let newdeps = map (\dep -> if new == dep then old else dep) deps in WithDroppedDeps (NamedP pn newdeps (unsafeCoerceP body)) ddeps :> Rename old new -forceCommutePrim :: (Commute p, Merge p, Invert p, Effect p, FromPrim p)+forceCommutePrim :: (Merge p, Invert p, Effect p, FromPrim p) => (PrimOf p :> WDDNamed p) wX wY -> (WDDNamed p :> FL (PrimOf p)) wX wY forceCommutePrim (p :> q) =@@ -468,7 +471,7 @@ q' :/\: invp' -> q' :> effect (invert invp') -forceCommutesPrim :: (Commute p, Merge p, Invert p, Effect p, FromPrim p)+forceCommutesPrim :: (Merge p, Invert p, Effect p, FromPrim p) => (PrimOf p :> FL (WDDNamed p)) wX wY -> (FL (WDDNamed p) :> FL (PrimOf p)) wX wY forceCommutesPrim (p :> NilFL) = NilFL :> (p :>: NilFL)@@ -477,7 +480,7 @@ q' :> p' -> case forceCommutessPrim ( p' :> qs) of qs' :> p'' -> (q' :>: qs') :> p'' -forceCommutessPrim :: (Commute p, Merge p, Invert p, Effect p, FromPrim p)+forceCommutessPrim :: (Merge p, Invert p, Effect p, FromPrim p) => (FL (PrimOf p) :> FL (WDDNamed p)) wX wY -> (FL (WDDNamed p) :> FL (PrimOf p)) wX wY forceCommutessPrim (NilFL :> qs) = qs :> NilFL@@ -487,7 +490,7 @@ case forceCommutesPrim (p :> qs') of qs'' :> p' -> qs'' :> (p' +>+ ps') -forceCommutess :: (Commute p, Merge p, Invert p, Effect p, FromPrim p)+forceCommutess :: (Merge p, Invert p, Effect p, FromPrim p) => (FL (RebaseFixup p) :> FL (WDDNamed p)) wX wY -> (FL (WDDNamed p) :> FL (RebaseFixup p)) wX wY forceCommutess (NilFL :> qs) = qs :> NilFL@@ -506,7 +509,7 @@ -- the main repository, together with residual fixups that need -- to go back into the rebase state (unless the rebase is now finished). -- Any fixups associated with the patch will turn into conflicts.-extractRebaseSelect :: (Commute p, Merge p, Invert p, Effect p, FromPrim p, PrimPatchBase p)+extractRebaseSelect :: (Merge p, Invert p, Effect p, FromPrim p, PrimPatchBase p) => FL (RebaseSelect p) wX wY -> (FL (WDDNamed p) :> FL (RebaseFixup p)) wX wY extractRebaseSelect NilFL = NilFL :> NilFL@@ -522,7 +525,7 @@ -- signature to be compatible with extractRebaseSelect -- | Like 'extractRebaseSelect', but any fixups are "reified" into a separate patch. reifyRebaseSelect :: forall p wX wY- . (PrimPatchBase p, Commute p, Merge p, Invert p, Effect p, FromPrim p)+ . (PrimPatchBase p, FromPrim p) => FL (RebaseSelect p) wX wY -> IO ((FL (WDDNamed p) :> FL (RebaseFixup p)) wX wY) reifyRebaseSelect rs = do res <- concatFL <$> mapFL_FL_M reifyOne rs@@ -553,7 +556,7 @@ instance CommuteNoConflicts (RebaseChange p) where commuteNoConflicts _ = impossible -instance IsHunk p => IsHunk (RebaseChange p) where+instance 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
@@ -1,25 +1,27 @@ module Darcs.Patch.RepoPatch ( RepoPatch ) where +import Darcs.Patch.Annotate ( Annotate )+import Darcs.Patch.Apply ( Apply, ApplyState )+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 ) import Darcs.Patch.Inspect ( PatchInspect )+import Darcs.Patch.Invert ( Invert ) import Darcs.Patch.Matchable ( Matchable ) 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.Repair ( RepairToFL, Check )-import Darcs.Patch.Show ( ShowPatch )+import Darcs.Patch.Show ( ShowPatch, ShowContextPatch ) -class (Patchy p, Merge p, Effect p, IsHunk p,- PatchInspect p, ReadPatch p, ShowPatch p,+class (Apply p, Commute p, Invert p, Merge p, Effect p, IsHunk p,+ PatchInspect p, ReadPatch p, ShowPatch p, ShowContextPatch p, FromPrim p, Conflict p, CommuteNoConflicts p, Check p, RepairToFL p, PatchListFormat p,- PrimPatchBase p, Patchy (PrimOf p), IsHunk (PrimOf p),- Matchable p+ PrimPatchBase p, IsHunk (PrimOf p),+ Matchable p, Annotate p, ApplyState p ~ ApplyState (PrimOf p) ) => RepoPatch p
@@ -15,7 +15,7 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, EmptyDataDecls, StandaloneDeriving #-}+{-# LANGUAGE EmptyDataDecls, StandaloneDeriving #-} module Darcs.Patch.Set ( PatchSet(..)@@ -26,8 +26,8 @@ , tags , emptyPatchSet , appendPSFL- , newset2RL- , newset2FL+ , patchSet2RL+ , patchSet2FL , patchSetfMap ) where @@ -89,18 +89,18 @@ showDict2 = ShowDictClass --- |'newset2RL' takes a 'PatchSet' and returns an equivalent, linear 'RL' of+-- |'patchSet2RL' takes a 'PatchSet' and returns an equivalent, linear 'RL' of -- patches.-newset2RL :: PatchSet rt p wStart wX -> RL (PatchInfoAnd rt p) wStart wX-newset2RL (PatchSet ts ps) = concatRL (mapRL_RL ts2rl ts) +<+ ps+patchSet2RL :: PatchSet rt p wStart wX -> RL (PatchInfoAnd rt p) wStart wX+patchSet2RL (PatchSet ts ps) = concatRL (mapRL_RL ts2rl ts) +<+ ps where 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+-- |'patchSet2FL' takes a 'PatchSet' and returns an equivalent, linear 'FL' of -- patches.-newset2FL :: PatchSet rt p wStart wX -> FL (PatchInfoAnd rt p) wStart wX-newset2FL = reverseRL . newset2RL+patchSet2FL :: PatchSet rt p wStart wX -> FL (PatchInfoAnd rt p) wStart wX+patchSet2FL = reverseRL . patchSet2RL -- |'appendPSFL' takes a 'PatchSet' and a 'FL' of patches that "follow" the -- PatchSet, and concatenates the patches into the PatchSet.@@ -128,4 +128,4 @@ 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+patchSetfMap f = sequence . mapRL f . patchSet2RL
@@ -15,59 +15,59 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.Patch.Show ( ShowPatchBasic(..)+ , displayPatch+ , ShowPatchFor(..) , ShowPatch(..)- , showNamedPrefix+ , ShowContextPatch(..) , formatFileName ) where import Prelude () import Darcs.Prelude -import Prelude hiding ( pi )+import qualified Data.ByteString.Char8 as BC ( unpack ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.ApplyMonad ( ApplyMonad ) import Darcs.Patch.Format ( FileNameFormat(..) )-import Darcs.Patch.Info ( PatchInfo, showPatchInfo )-import Darcs.Patch.Witnesses.Ordered ( FL )+import Darcs.Patch.Witnesses.Ordered ( FL, mapFL ) +import Darcs.Util.ByteString ( packStringToUTF8, encodeLocale ) import Darcs.Util.English ( plural, Noun(Noun) )-import Darcs.Util.Path ( FileName, fn2ps, encodeWhite, fn2fp )-import Darcs.Util.Printer ( Doc, vcat, blueText, ($$), text, packedString )+import Darcs.Util.Path ( FileName, encodeWhite, fn2fp )+import Darcs.Util.Printer ( Doc, vcat, text, packedString ) -showNamedPrefix :: PatchInfo -> [PatchInfo] -> Doc-showNamedPrefix n d = showPatchInfo n- $$ blueText "<"- $$ vcat (map showPatchInfo d)- $$ blueText ">"+data ShowPatchFor = ForDisplay | ForStorage -class ShowPatchBasic p where- showPatch :: p wX wY -> Doc+displayPatch :: ShowPatchBasic p => p wX wY -> Doc+displayPatch p = showPatch ForDisplay p -class ShowPatchBasic p => ShowPatch p where- showNicely :: p wX wY -> Doc- showNicely = showPatch+class ShowPatchBasic p where+ showPatch :: ShowPatchFor -> p wX wY -> Doc +class ShowPatchBasic p => ShowContextPatch p where -- | showContextPatch is used to add context to a patch, as diff -- -u does. Thus, it differs from showPatch only for hunks. It is -- 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,- ApplyMonad (ApplyState p) m)- => p wX wY -> m Doc- showContextPatch p = return $ showPatch p+ showContextPatch :: (ApplyMonad (ApplyState p) m)+ => ShowPatchFor -> p wX wY -> m Doc +-- This class is used only for user interaction, not for storage+class ShowPatchBasic p => ShowPatch p where+ showNicely :: p wX wY -> Doc+ showNicely = showPatch ForDisplay+ description :: p wX wY -> Doc- description = showPatch+ description = showPatch ForDisplay summary :: p wX wY -> Doc summaryFL :: FL p wX wY -> Doc+ summaryFL = vcat . mapFL summary thing :: p wX wY -> String thing _ = "patch"@@ -75,6 +75,19 @@ things :: p wX wY -> String things x = plural (Noun $ thing x) "" +-- | Format a 'FileName' to a 'Doc' according to the given 'FileNameFormat'.+--+-- NOTE: This is not only used for display but also to format patch files. This is+-- why we have to do the white space encoding here.+-- See 'Darcs.Repository.Hashed.writePatchIfNecessary'.+--+-- Besides white space encoding, for 'NewFormat' we just pack it into a 'Doc'. For+-- 'OldFormat' we must emulate the non-standard darcs-1 encoding of file paths: it+-- is an UTF8 encoding of the raw byte stream, interpreted as code points.+--+-- See also 'Darcs.Patch.Show.readFileName'. formatFileName :: FileNameFormat -> FileName -> Doc-formatFileName OldFormat = packedString . fn2ps+formatFileName OldFormat = packedString . packStringToUTF8 . BC.unpack .+ encodeLocale . encodeWhite . fn2fp formatFileName NewFormat = text . encodeWhite . fn2fp+formatFileName UserFormat = text . fn2fp
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP, RankNTypes, GADTs, ViewPatterns #-}+{-# LANGUAGE ViewPatterns #-} {-# OPTIONS_GHC -fno-warn-unused-imports #-} -- Copyright (C) 2009 Ganesh Sittampalam --@@ -22,7 +22,13 @@ -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -- SOFTWARE. -module Darcs.Patch.Split ( Splitter(..), rawSplitter, noSplitter, primSplitter, reversePrimSplitter ) where+module Darcs.Patch.Split+ ( Splitter(..)+ , rawSplitter+ , noSplitter+ , primSplitter+ , reversePrimSplitter+ ) where import Prelude () import Darcs.Prelude@@ -33,14 +39,16 @@ import Darcs.Patch.Witnesses.Sealed import Darcs.Patch.FileHunk ( FileHunk(..), IsHunk(..) )-import Darcs.Patch.Patchy ( ReadPatch(..), showPatch, ShowPatch(..), Invert(..) )-import Darcs.Patch.Invert (invertFL)+import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.Show ( showPatch, ShowPatch(..) )+import Darcs.Patch.Invert( Invert(..), invertFL ) import Darcs.Patch.Prim ( PrimPatch, canonize, canonizeFL, primFromHunk ) import Darcs.Patch.ReadMonads ( parseStrictly ) import Darcs.Patch.Read ()+import Darcs.Patch.Show ( ShowPatchFor(ForDisplay) ) import Darcs.Patch.Viewing () -import Darcs.Util.Printer ( renderPS, RenderMode(..) )+import Darcs.Util.Printer ( renderPS ) import qualified Darcs.Util.Diff as D ( DiffAlgorithm ) import qualified Data.ByteString as B@@ -63,23 +71,20 @@ -- Prim (you shouldn't go editing named patches, you'll break them!) -- However you might want to compose splitters for FilePatchType to make -- splitters for Prim etc, and the generality doesn't cost anything.-data Splitter p- = Splitter {- 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- -- Typically, the list returned by applySplitter will not- -- be in the simplest possible form (since the user will have- -- deliberately added extra stuff). Once the user has selected- -- the pieces they want, we need to make sure that we eliminate- -- any remaining redundancy in the selected pieces, otherwise- -- 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 . FL p wX wY- -> FL p wX wY- }+data Splitter p = Splitter+ { 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+ -- Typically, the list returned by applySplitter will not+ -- be in the simplest possible form (since the user will have+ -- deliberately added extra stuff). Once the user has selected+ -- the pieces they want, we need to make sure that we eliminate+ -- any remaining redundancy in the selected pieces, otherwise+ -- 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. FL p wX wY -> FL p wX wY+ } {- Some facts that probably ought to be true about splitters: should make some QC properties@@ -94,22 +99,21 @@ withEditedHead :: Invert p => p wX wY -> p wX wZ -> FL p wX wY withEditedHead p res = res :>: invert res :>: p :>: NilFL --- |This generic splitter just lets the user edit the printed representation of the patch--- Should not be used expect for testing and experimentation.+-- |This generic splitter just lets the user edit the printed representation of the+-- patch. Should not be used expect for testing and experimentation. rawSplitter :: (ShowPatch p, ReadPatch p, Invert p) => Splitter p-rawSplitter = Splitter {- applySplitter =- \p -> Just (renderPS Standard . showPatch $ p,- \str -> case parseStrictly readPatch' str of- Just (Sealed res, _) -> Just (withEditedHead p res)- _ -> Nothing- )- ,canonizeSplit = id- }+rawSplitter = Splitter+ { applySplitter = \p ->+ Just (renderPS . showPatch ForDisplay $ p+ ,\str -> case parseStrictly readPatch' str of+ Just (Sealed res, _) -> Just (withEditedHead p res)+ _ -> Nothing)+ , 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.+-- |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 = id } @@ -118,16 +122,17 @@ -> Maybe (B.ByteString, B.ByteString -> Maybe (FL prim wX wY)) doPrimSplit da = doPrimSplit_ da True explanation where- explanation = map BC.pack- [ "Interactive hunk edit:"- , " - Edit the section marked 'AFTER'"- , " - Arbitrary editing is supported"- , " - 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"- , ""- ]+ explanation =+ map BC.pack+ [ "Interactive hunk edit:"+ , " - Edit the section marked 'AFTER'"+ , " - Arbitrary editing is supported"+ , " - 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"+ , ""+ ] doPrimSplit_ :: (PrimPatch prim, IsHunk p) => D.DiffAlgorithm@@ -161,9 +166,8 @@ (ys, _:zs) -> Just (ys, zs) doPrimSplit_ _ _ _ _ = Nothing --- |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.+-- |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 => D.DiffAlgorithm -> Splitter p primSplitter da = Splitter { applySplitter = doPrimSplit da , canonizeSplit = canonizeFL da }@@ -178,16 +182,17 @@ return (text, parser') where reverseExplanation =- 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 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"- , " - To revert only a part of a removal, copy back the part you want to retain"- , ""- ]+ 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 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"+ , " - To revert only a part of a removal, copy back the part you want to retain"+ , ""+ ] reversePrimSplitter :: PrimPatch prim => D.DiffAlgorithm -> Splitter prim reversePrimSplitter da = Splitter { applySplitter = doReversePrimSplit da
@@ -6,10 +6,11 @@ import Prelude () import Darcs.Prelude -import Darcs.Util.Path ( fn2fp, FileName )+import Darcs.Util.Path ( fn2fp ) import Darcs.Patch.Conflict ( Conflict(..), IsConflictedPrim(IsC), ConflictState(..) )-import Darcs.Patch.Effect ( Effect )+import Darcs.Patch.Format ( FileNameFormat(UserFormat) ) import Darcs.Patch.Prim.Class ( PrimDetails(..), PrimPatchBase )+import Darcs.Patch.Show ( formatFileName ) import Darcs.Patch.SummaryData ( SummDetail(..), SummOp(..) ) import Darcs.Patch.Witnesses.Ordered ( FL, mapFL ) @@ -22,18 +23,14 @@ plainSummaryPrim :: PrimDetails prim => prim wX wY -> Doc plainSummaryPrim = vcat . map (summChunkToLine False) . genSummary . (:[]) . 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+plainSummaryPrims :: PrimDetails prim => Bool -> FL prim wX wY -> Doc+plainSummaryPrims machineReadable =+ vcat . map (summChunkToLine machineReadable) . genSummary . mapFL (IsC Okay) -plainSummary :: (Conflict e, Effect e, PrimPatchBase e) => e wX wY -> Doc+plainSummary :: (Conflict e, PrimPatchBase e) => e wX wY -> Doc plainSummary = vcat . map (summChunkToLine False) . genSummary . conflictedEffect -xmlSummary :: (Effect p, Conflict p, PrimPatchBase p) => p wX wY -> Doc+xmlSummary :: (Conflict p, PrimPatchBase p) => p wX wY -> Doc xmlSummary p = text "<summary>" $$ (vcat . map summChunkToXML . genSummary . conflictedEffect $ p) $$ text "</summary>"@@ -62,7 +59,7 @@ combine (x1@(SummChunk d1 c1) : x2@(SummChunk d2 c2) : ss) = case combineDetail d1 d2 of Nothing -> x1 : combine (x2:ss)- Just d3 -> combine $ SummChunk d3 (combineConflitStates c1 c2) : ss+ Just d3 -> combine $ SummChunk d3 (combineConflictStates c1 c2) : ss combine (x:ss) = x : combine ss combine [] = [] --@@ -71,11 +68,11 @@ return $ SummFile o3 f1 (r1 + r2) (a1 + a2) (x1 + x2) combineDetail _ _ = Nothing --- combineConflitStates Conflicted _ = Conflicted- combineConflitStates _ Conflicted = Conflicted- combineConflitStates Duplicated _ = Duplicated- combineConflitStates _ Duplicated = Duplicated- combineConflitStates Okay Okay = Okay+ combineConflictStates Conflicted _ = Conflicted+ combineConflictStates _ Conflicted = Conflicted+ combineConflictStates Duplicated _ = Duplicated+ combineConflictStates _ Duplicated = Duplicated+ combineConflictStates Okay Okay = Okay -- Don't combine AddFile and RmFile: (maybe an old revision of) darcs -- allows a single patch to add and remove the same file, see issue 185 combineOp SummAdd SummRm = Nothing@@ -101,7 +98,7 @@ xconf Okay t x = text ('<':t++">") $$ x $$ text ("</"++t++">") xconf Conflicted t x = text ('<':t++" conflict='true'>") $$ x $$ text ("</"++t++">") xconf Duplicated t x = text ('<':t++" duplicate='true'>") $$ x $$ text ("</"++t++">")- xfn = escapeXML . dropDotSlash .fn2fp+ xfn = escapeXML . dropDotSlash . fn2fp -- xad 0 = empty xad a = text "<added_lines num='" <> text (show a) <> text "'/>"@@ -113,18 +110,18 @@ summChunkToLine :: Bool -> SummChunk -> Doc summChunkToLine machineReadable (SummChunk detail c) = case detail of- SummRmDir f -> lconf c "R" $ text (fn2fp f) <> text "/"- SummAddDir f -> lconf c "A" $ text (fn2fp f) <> text "/"- SummFile SummRm f _ _ _ -> lconf c "R" $ text (fn2fp f)- SummFile SummAdd f _ _ _ -> lconf c "A" $ text (fn2fp f)+ SummRmDir f -> lconf c "R" $ formatFileName UserFormat f <> text "/"+ SummAddDir f -> lconf c "A" $ formatFileName UserFormat f <> text "/"+ SummFile SummRm f _ _ _ -> lconf c "R" $ formatFileName UserFormat f+ SummFile SummAdd f _ _ _ -> lconf c "A" $ formatFileName UserFormat f SummFile SummMod f r a x- | machineReadable -> lconf c "M" $ text (fn2fp f)- | otherwise -> lconf c "M" $ text (fn2fp f) <+> rm r <+> ad a <+> rp x+ | machineReadable -> lconf c "M" $ formatFileName UserFormat f+ | otherwise -> lconf c "M" $ formatFileName UserFormat f <+> rm r <+> ad a <+> rp x SummMv f1 f2- | machineReadable -> text "F " <> text (fn2fp f1)- $$ text "T " <> text (fn2fp f2)- | otherwise -> text " " <> text (fn2fp f1)- <> text " -> " <> text (fn2fp f2)+ | machineReadable -> text "F " <> formatFileName UserFormat f1+ $$ text "T " <> formatFileName UserFormat f2+ | otherwise -> text " " <> formatFileName UserFormat f1+ <> text " -> " <> formatFileName UserFormat f2 SummNone -> case c of Okay -> empty _ -> lconf c "" empty
@@ -1,8 +1,7 @@ module Darcs.Patch.TokenReplace- (- tryTokInternal+ ( tryTokReplace , forceTokReplace- , breakOutToken+ , annotateReplace , breakToTokens , defaultToks ) where@@ -12,61 +11,91 @@ import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC-import Data.Maybe ( isNothing ) -import Darcs.Util.ByteString ( substrPS, linesPS, unlinesPS ) import Darcs.Patch.RegChars ( regChars ) --- | breakOutToken takes a String of token chars and an input ByteString, and--- returns the ByteString triple of (beforeToken, token, afterToken).+-- | @breakOutToken tokChars input@ splits the @input@ 'ByteString' into+-- @'Just' (before, token, after)@, where @token@ is the first non-empty+-- substring consisting only of 'Char's in @tokChars@, or 'Nothing' if no token+-- was found. The 'Char's in @tokChars@ should not have code points larger than+-- 255 (0xff). breakOutToken :: String -> BC.ByteString- -> (BC.ByteString, BC.ByteString, BC.ByteString)-breakOutToken tokChars input =- let isTokChar = regChars tokChars- (before, tokAndRest) = BC.break isTokChar input- (tok, remaining) = BC.break (not . isTokChar) tokAndRest in- (before, tok, remaining)---- | tryTokInternal takes a String of token chars, an oldToken ByteString, a--- newToken ByteString and returns the list of token-delimited ByteStrings,--- with any tokens matching oldToken being replaced by newToken. If newToken is--- already in the input, we return Nothing.-tryTokInternal :: String -> B.ByteString -> B.ByteString -> B.ByteString- -> Maybe [B.ByteString]-tryTokInternal _ oldToken newToken input- | isNothing (substrPS oldToken input) &&- isNothing (substrPS newToken input) = Just [ input ]-tryTokInternal tokChars oldToken newToken input =- let (before, tok, remaining) = breakOutToken tokChars input in- case tryTokInternal tokChars oldToken newToken remaining of- Nothing -> Nothing- Just rest | tok == oldToken -> Just $ before : newToken : rest- | tok == newToken -> Nothing- | otherwise -> Just $ before : tok : rest---- | forceTokReplace replaces all occurrences of the old token with the new--- token, throughout the input ByteString.-forceTokReplace :: String -> B.ByteString -> B.ByteString -> B.ByteString- -> B.ByteString-forceTokReplace tokChars oldToken newToken = forceReplaceLines+ -> Maybe (BC.ByteString, BC.ByteString, BC.ByteString)+breakOutToken tokChars input+ | not (B.null tok) = Just (before, tok, remaining)+ | otherwise = Nothing where- forceReplaceLines = unlinesPS . map forceReplace . linesPS- breakOutAllTokens input | B.null input = []- breakOutAllTokens input =- let (before, tok, remaining) = breakOutToken tokChars input in- before : tok : breakOutAllTokens remaining+ isTokChar = regChars tokChars+ (before, tokAndRest) = BC.break isTokChar input+ (tok, remaining) = BC.break (not . isTokChar) tokAndRest - forceReplace = B.concat . map replaceMatchingToken . breakOutAllTokens+-- | @tryTokReplace tokChars old new input@ tries to find the token @old@ and+-- replace it with the token @new@ everywhere in the @input@, returning 'Just'+-- the modified @input@, unless the token @new@ is already in the @input@ in+-- which case 'Nothing' is returned. A token is a sequence of bytes that match+-- the class defined by @tokChars@. This function is supposed to work+-- efficiently with large @input@s i.e. whole files.+tryTokReplace :: String -> B.ByteString -> B.ByteString+ -> B.ByteString -> Maybe B.ByteString+tryTokReplace tokChars old new+ | B.null old = bug "tryTokInternal called with empty old token"+ | BC.any (not . isTokChar) old = bug "tryTokInternal called with old non-token"+ | BC.any (not . isTokChar) new = bug "tryTokInternal called with new non-token"+ | otherwise = fmap B.concat . loop 0+ where+ isTokChar = regChars tokChars+ loop !from input =+ case BC.findIndex isTokChar (B.drop from input) of+ Nothing -> Just [input]+ Just start ->+ case BC.span isTokChar (B.drop (from + start) input) of+ (tok, rest)+ | tok == old ->+ (B.take (from + start) input :).(new :) <$> loop 0 rest+ | tok == new -> Nothing+ | otherwise ->+ loop (from + start + B.length tok) input - replaceMatchingToken input | input == oldToken = newToken- | otherwise = input+-- | @forceTokReplace tokChars old new input@ replaces all occurrences of+-- the @old@ token with the @new@ one, throughout the @input@.+forceTokReplace :: String -> B.ByteString -> B.ByteString+ -> B.ByteString -> B.ByteString+forceTokReplace tokChars old new+ | B.null old = bug "tryTokInternal called with empty old token"+ | BC.any (not . isTokChar) old = bug "tryTokInternal called with old non-token"+ | BC.any (not . isTokChar) new = bug "tryTokInternal called with new non-token"+ | otherwise = B.concat . loop 0+ where+ isTokChar = regChars tokChars+ len = B.length old+ loop !from input =+ case B.breakSubstring old (B.drop from input) of+ (before, match)+ | B.null match -> [input] -- not found+ | B.null before || not (isTokChar (BC.last before))+ , B.length match == len || not (isTokChar (BC.index match len)) ->+ -- found and is token+ B.take (from + B.length before) input : new :+ loop 0 (B.drop len match)+ | otherwise ->+ -- found but not a token+ loop (from + B.length before + len) input --- break a single bytestring into tokens+-- | Check if a token replace operation touches the given line.+annotateReplace :: String -> B.ByteString -> B.ByteString -> B.ByteString -> Bool+annotateReplace tokChars old new input =+ case breakOutToken tokChars input of+ Just (_, tok, remaining) ->+ (tok == old || annotateReplace tokChars old new remaining)+ Nothing -> False++-- | Break a 'Bytestring' into tokens, according to 'defaultToks',+-- discarding non-tokens. breakToTokens :: BC.ByteString -> [BC.ByteString]-breakToTokens input | B.null input = [] breakToTokens input =- let (_, tok, remaining) = breakOutToken defaultToks input in- tok : breakToTokens remaining+ case breakOutToken defaultToks input of+ Nothing -> []+ Just (_, tok, remaining) -> tok : breakToTokens remaining defaultToks :: String defaultToks = "A-Za-z_0-9"
@@ -15,98 +15,122 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}---module Darcs.Patch.TouchesFiles ( lookTouch, chooseTouching, choosePreTouching,- selectTouching,- deselectNotTouching, selectNotTouching,- ) where+module Darcs.Patch.TouchesFiles+ ( lookTouch+ , chooseTouching+ , choosePreTouching+ , selectTouching+ , deselectNotTouching+ , selectNotTouching+ ) where -import Prelude () import Darcs.Prelude+import Prelude () -import Data.List ( isSuffixOf, nub )+import Data.List (isSuffixOf, nub) -import Darcs.Patch.Choices ( PatchChoices, Label, LabelledPatch,- patchChoices, label, getChoices,- forceFirsts, forceLasts, lpPatch,- )-import Darcs.Patch ( Patchy, invert )-import Darcs.Patch.Apply ( ApplyState, applyToFilePaths, effectOnFilePaths )-import Darcs.Patch.Inspect ( PatchInspect )-import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), mapFL_FL, (+>+) )-import Darcs.Patch.Witnesses.Sealed ( Sealed, seal )-import Darcs.Util.Tree( Tree )+import Darcs.Patch.Apply+ (Apply, ApplyState, applyToFilePaths, effectOnFilePaths)+import Darcs.Patch.Choices+ (PatchChoices, Label, LabelledPatch, patchChoices, label,+ getChoices, forceFirsts, forceLasts, unLabel)+import Darcs.Patch.Commute (Commute)+import Darcs.Patch.Inspect (PatchInspect)+import Darcs.Patch.Invert (invert, Invert)+import Darcs.Patch.Witnesses.Ordered+ (FL(..), (:>)(..), mapFL_FL, (+>+))+import Darcs.Patch.Witnesses.Sealed (Sealed, seal)+import Darcs.Util.Tree (Tree) -labelTouching :: (Patchy p, PatchInspect p, ApplyState p ~ Tree) => Bool- -> [FilePath] -> FL (LabelledPatch p) wX wY -> [Label]+labelTouching+ :: (Apply p, PatchInspect p, ApplyState p ~ Tree)+ => Bool -> [FilePath] -> FL (LabelledPatch p) wX wY -> [Label] labelTouching _ _ NilFL = [] labelTouching wantTouching fs (lp :>: lps) =- case lookTouchOnlyEffect fs (lpPatch lp) of- (doesTouch, fs') ->- let rest = labelTouching wantTouching fs' lps- in (if doesTouch == wantTouching then (label lp :) else id) rest+ case lookTouchOnlyEffect fs (unLabel lp) of+ (doesTouch, fs') ->+ let rest = labelTouching wantTouching fs' lps+ in (if doesTouch == wantTouching+ then (label lp :)+ else id)+ rest -labelNotTouchingFM :: (PatchInspect p, Patchy p, ApplyState p ~ Tree)- => [FilePath] -> PatchChoices p wX wY -> [Label]-labelNotTouchingFM files pc = case getChoices pc of+labelNotTouchingFM+ :: (Apply p, Commute p, PatchInspect p, ApplyState p ~ Tree)+ => [FilePath] -> PatchChoices p wX wY -> [Label]+labelNotTouchingFM files pc =+ case getChoices pc of fc :> mc :> _ -> labelTouching False (map fix files) (fc +>+ mc) -selectTouching :: (Patchy p, PatchInspect p, ApplyState p ~ Tree)- => Maybe [FilePath] -> PatchChoices p wX wY -> PatchChoices p wX wY+selectTouching+ :: (Apply p, Commute p, PatchInspect p, ApplyState p ~ Tree)+ => Maybe [FilePath] -> PatchChoices p wX wY -> PatchChoices p wX wY selectTouching Nothing pc = pc selectTouching (Just files) pc = forceFirsts xs pc- where xs = case getChoices pc of- _ :> mc :> lc -> labelTouching True (map fix files) (mc +>+ lc)+ where+ xs =+ case getChoices pc of+ _ :> mc :> lc -> labelTouching True (map fix files) (mc +>+ lc) -deselectNotTouching :: (Patchy p, PatchInspect p, ApplyState p ~ Tree)- => Maybe [FilePath] -> PatchChoices p wX wY -> PatchChoices p wX wY+deselectNotTouching+ :: (Apply p, Commute p, PatchInspect p, ApplyState p ~ Tree)+ => Maybe [FilePath] -> PatchChoices p wX wY -> PatchChoices p wX wY deselectNotTouching Nothing pc = pc-deselectNotTouching (Just files) pc = forceLasts (labelNotTouchingFM files pc) pc+deselectNotTouching (Just files) pc =+ forceLasts (labelNotTouchingFM files pc) pc -selectNotTouching :: (Patchy p, PatchInspect p, ApplyState p ~ Tree)- => Maybe [FilePath] -> PatchChoices p wX wY -> PatchChoices p wX wY+selectNotTouching+ :: (Apply p, Commute p, PatchInspect p, ApplyState p ~ Tree)+ => Maybe [FilePath] -> PatchChoices p wX wY -> PatchChoices p wX wY selectNotTouching Nothing pc = pc selectNotTouching (Just files) pc = forceFirsts (labelNotTouchingFM files pc) pc fix :: FilePath -> FilePath-fix f | "/" `isSuffixOf` f = fix $ init f+fix f+ | "/" `isSuffixOf` f = fix $ init f fix "" = "." fix "." = "." fix f = "./" ++ f -chooseTouching :: (Patchy p, PatchInspect p, ApplyState p ~ Tree)- => Maybe [FilePath] -> FL p wX wY -> Sealed (FL p wX)+chooseTouching+ :: (Apply p, Commute p, PatchInspect p, ApplyState p ~ Tree)+ => Maybe [FilePath] -> FL p wX wY -> Sealed (FL p wX) chooseTouching Nothing p = seal p-chooseTouching files p = case getChoices $ selectTouching files $ patchChoices p of- fc :> _ :> _ -> seal $ mapFL_FL lpPatch fc+chooseTouching files p =+ case getChoices $ selectTouching files $ patchChoices p of+ fc :> _ :> _ -> seal $ mapFL_FL unLabel fc -choosePreTouching :: (Patchy p, PatchInspect p, ApplyState p ~ Tree)- => Maybe [FilePath] -> FL p wX wY -> Sealed (FL p wX)-choosePreTouching files patch = chooseTouching filesBeforePatch patch where+choosePreTouching+ :: (Apply p, Commute p, Invert p, PatchInspect p, ApplyState p ~ Tree)+ => Maybe [FilePath] -> FL p wX wY -> Sealed (FL p wX)+choosePreTouching files patch = chooseTouching filesBeforePatch patch+ where filesBeforePatch = effectOnFilePaths (invert patch) <$> files -lookTouchOnlyEffect :: (Patchy p, PatchInspect p, ApplyState p ~ Tree) => [FilePath] -> p wX wY- -> (Bool, [FilePath])-lookTouchOnlyEffect fs p = (wasTouched, fs') where+lookTouchOnlyEffect+ :: (Apply p, ApplyState p ~ Tree)+ => [FilePath] -> p wX wY -> (Bool, [FilePath])+lookTouchOnlyEffect fs p = (wasTouched, fs')+ where (wasTouched, _, fs', _) = lookTouch Nothing fs p --lookTouch :: (Patchy p, PatchInspect p, ApplyState p ~ Tree) => Maybe [(FilePath, FilePath)]- -> [FilePath] -> p wX wY- -> (Bool, [FilePath], [FilePath], [(FilePath, FilePath)])+lookTouch+ :: (Apply p, ApplyState p ~ Tree)+ => Maybe [(FilePath, FilePath)]+ -> [FilePath]+ -> p wX wY+ -> (Bool, [FilePath], [FilePath], [(FilePath, FilePath)]) lookTouch renames fs p = (anyTouched, touchedFs, fs', renames')- where- touchedFs = nub . concatMap fsAffectedBy $ affected- fsAffectedBy af = filter (affectedBy af) fs- anyTouched = length touchedFs > 0- affectedBy :: FilePath -> FilePath -> Bool- touched `affectedBy` f = touched == f- || touched `isSubPathOf` f- || f `isSubPathOf` touched- isSubPathOf :: FilePath -> FilePath -> Bool- path `isSubPathOf` parent = case splitAt (length parent) path of- (path', '/':_) -> path' == parent- _ -> False- (affected, fs', renames') = applyToFilePaths p renames fs+ where+ touchedFs = nub . concatMap fsAffectedBy $ affected+ fsAffectedBy af = filter (affectedBy af) fs+ anyTouched = length touchedFs > 0+ affectedBy :: FilePath -> FilePath -> Bool+ touched `affectedBy` f =+ touched == f || touched `isSubPathOf` f || f `isSubPathOf` touched+ isSubPathOf :: FilePath -> FilePath -> Bool+ path `isSubPathOf` parent =+ case splitAt (length parent) path of+ (path', '/':_) -> path' == parent+ _ -> False+ (affected, fs', renames') = applyToFilePaths p renames fs
@@ -1,8 +1,8 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Darcs.Patch.V1 ( RepoPatchV1 ) where +import Darcs.Patch.Annotate ( Annotate ) import Darcs.Patch.Matchable ( Matchable )-import Darcs.Patch.Patchy ( Patchy ) import Darcs.Patch.Prim ( PrimPatch ) import Darcs.Patch.RepoPatch ( RepoPatch ) @@ -13,6 +13,5 @@ import Darcs.Patch.V1.Show () import Darcs.Patch.V1.Viewing () -instance PrimPatch prim => Patchy (RepoPatchV1 prim) instance PrimPatch prim => Matchable (RepoPatchV1 prim)-instance PrimPatch prim => RepoPatch (RepoPatchV1 prim)+instance (PrimPatch prim, Annotate prim) => RepoPatch (RepoPatchV1 prim)
@@ -4,6 +4,7 @@ import Prelude () import Darcs.Prelude +import Darcs.Patch.Annotate ( Annotate(..) ) import Darcs.Patch.Apply ( ApplyState, Apply, apply ) import Darcs.Patch.Prim ( PrimPatch, applyPrimFL ) import Darcs.Patch.Repair ( RepairToFL, applyAndTryToFixFL,@@ -12,7 +13,6 @@ import Darcs.Patch.V1.Commute () import Darcs.Patch.V1.Core ( RepoPatchV1(..) )- import Darcs.Patch.Witnesses.Ordered ( mapFL_FL ) @@ -23,3 +23,6 @@ instance PrimPatch prim => RepairToFL (RepoPatchV1 prim) where applyAndTryToFixFL (PP x) = mapMaybeSnd (mapFL_FL PP) `fmap` applyAndTryToFixFL x applyAndTryToFixFL x = do apply x; return Nothing++instance (PrimPatch prim, Annotate prim) => Annotate (RepoPatchV1 prim) where+ annotate = annotate . effect
@@ -16,9 +16,7 @@ -- Boston, MA 02110-1301, USA. {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP #-} - module Darcs.Patch.V1.Commute ( merge,@@ -36,9 +34,12 @@ import Darcs.Patch.Commute ( selfCommuter ) import Darcs.Patch.CommuteFn ( commuterIdFL, commuterFLId ) import Darcs.Util.Path ( FileName )+import Darcs.Util.Printer ( errorDoc ) import Darcs.Patch.Invert ( invertRL )-import Darcs.Patch.Merge ( Merge(..) )-import Darcs.Patch.Patchy ( Commute(..), PatchInspect(..), Invert(..) )+import Darcs.Patch.Merge ( Merge(..), naturalMerge )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Invert ( Invert(..) )+import Darcs.Patch.Inspect ( PatchInspect(..) ) import Darcs.Patch.V1.Core ( RepoPatchV1(..), isMerger, mergerUndo )@@ -57,11 +58,10 @@ import Darcs.Patch.V1.Show ( showPatch_ ) import Data.List ( nub, nubBy ) import Data.List.Ordered ( nubSort )-#include "impossible.h" import Darcs.Patch.Witnesses.Sealed ( Sealed(..) , mapSeal, unseal, FlippedSeal(..), mapFlipped , unsafeUnseal, unsafeUnsealFlipped )-import Darcs.Patch.Witnesses.Eq ( EqCheck(..), MyEq(..) )+import Darcs.Patch.Witnesses.Eq ( EqCheck(..), Eq2(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP, unsafeCoercePStart , unsafeCoercePEnd )@@ -182,7 +182,7 @@ -- "after" @z@), but we need to return both arms. We therefore commute -- @z@ and @y'@, to obtain a @z'@, which applies "after" @y'' == y@. Sealed y' -> case commute (z :> y') of- Nothing -> bugDoc $ text "merge_patches bug"+ Nothing -> errorDoc $ text "merge_patches bug" $$ showPatch_ y $$ showPatch_ z $$ showPatch_ y'@@ -272,17 +272,6 @@ 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- => (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')- guard $ unsafeCompare p1o p1 -- should be a redundant check- return $ invert ip2' :/\: p1'- {- A note about mergers and type witnesses ---------------------------------------@@ -313,7 +302,7 @@ -- rebuild it? actualMerge :: PrimPatch prim => (RepoPatchV1 prim :\/: RepoPatchV1 prim) wX wY -> Sealed (RepoPatchV1 prim wY)-actualMerge (p1 :\/: p2) = case elegantMerge (p1:\/:p2) of+actualMerge (p1 :\/: p2) = case naturalMerge (p1:\/:p2) of Just (_ :/\: p1') -> Sealed p1' Nothing -> merger "0.0" p2 p1 @@ -352,7 +341,7 @@ Just p1s' -> mapFlipped (:<:p2) $ reconcileUnwindings p p1s' tp2s Nothing ->- bugDoc $ text "in function reconcileUnwindings"+ errorDoc $ text "in function reconcileUnwindings" $$ text "Original patch:" $$ showPatch_ p _ -> bug "in reconcileUnwindings"@@ -381,7 +370,8 @@ 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'))+ effect (unsafeCoercePStart $ unsafeUnseal (glump09 p1 p2)) :+ map (unsafeCoercePStart . unsafeUnseal) (unravel p')) : rcs (p :>: passedby) ps Nothing -> rcs (p :>: passedby) ps _ -> impossible@@ -395,7 +385,7 @@ -- This type seems wrong - the most natural type for the result would seem to be--- [Sealed (FL Prim wX)], given the type of unwind.+-- [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 => RepoPatchV1 prim wX wY -> [Sealed (FL prim wY)]@@ -463,11 +453,11 @@ 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- $$ text "Unwound:"- $$ vcat (unseal (mapRL showPatch_) $ unwind p)+ _ -> errorDoc $ text "in function newUr"+ $$ text "Original patch:"+ $$ showPatch_ p+ $$ text "Unwound:"+ $$ vcat (unseal (mapRL showPatch_) $ unwind p) newUr op ps = case filter (\(_:<:p) -> isMerger p) $ headPermutationsRL ps of@@ -481,13 +471,13 @@ = Merger undo unwindings p1 p2 invert (PP p) = PP (invert p) -instance MyEq prim => MyEq (RepoPatchV1 prim) where+instance Eq2 prim => Eq2 (RepoPatchV1 prim) where unsafeCompare = eqPatches -instance MyEq prim => Eq (RepoPatchV1 prim wX wY) where+instance Eq2 prim => Eq (RepoPatchV1 prim wX wY) where (==) = unsafeCompare -eqPatches :: MyEq prim => RepoPatchV1 prim wX wY -> RepoPatchV1 prim wW wZ -> Bool+eqPatches :: Eq2 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 &&
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving #-} module Darcs.Patch.V1.Core ( RepoPatchV1(..), isMerger, mergerUndo@@ -7,9 +7,14 @@ import Prelude () import Darcs.Prelude -import Darcs.Patch.Format ( PatchListFormat(..), ListFormat(ListFormatV1) )+import Darcs.Patch.Format+ ( PatchListFormat(..)+ , ListFormat(ListFormatV1)+ ) import Darcs.Patch.Debug ( PatchDebug(..) )-import Darcs.Patch.Prim ( FromPrim(..), PrimOf, PrimPatchBase, PrimPatch )+import Darcs.Patch.Prim+ ( FromPrim(..), PrimPatchBase(..), PrimPatch+ ) import Darcs.Patch.Repair ( Check ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL )@@ -19,8 +24,6 @@ , appPrec, showsPrec2 ) -#include "impossible.h"- -- This haddock could be put on the individual bits of Merger instead -- once haddock supports doc comments on GADT constructors {- |@@ -95,3 +98,4 @@ -- no checks instance PatchDebug prim => PatchDebug (RepoPatchV1 prim)+
@@ -0,0 +1,116 @@+-- it is stupid that we need UndecidableInstances just to call another+-- type function (see instance Apply below which requires this)+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module Darcs.Patch.V1.Prim ( Prim(..) ) where++import Prelude ()+import Darcs.Prelude++import Data.Coerce ( coerce )++import Darcs.Patch.Annotate ( Annotate(..) )+import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute )+import Darcs.Patch.FileHunk ( IsHunk(..) )+import Darcs.Patch.Format+ ( PatchListFormat(..)+ , ListFormat(ListFormatV1)+ , FileNameFormat(OldFormat,UserFormat) )+import Darcs.Patch.Inspect ( PatchInspect )+import Darcs.Patch.Invert ( Invert )+import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.Repair ( RepairToFL(..) )+import Darcs.Patch.Show+ ( ShowPatchBasic(..)+ , ShowPatchFor(..)+ , ShowPatch(..)+ , ShowContextPatch(..)+ )+import Darcs.Patch.Summary ( plainSummaryPrim, plainSummaryPrims )++import Darcs.Patch.Witnesses.Eq ( Eq2 )+import Darcs.Patch.Witnesses.Show+ ( Show1(..), Show2(..)+ , ShowDict(ShowDictClass)+ , appPrec, showsPrec2+ )+import Darcs.Patch.Witnesses.Sealed ( Sealed(..) )++import Darcs.Patch.Prim.Class+ ( PrimConstruct(..), PrimCanonize(..)+ , PrimClassify(..), PrimDetails(..)+ , PrimShow(..), PrimRead(..)+ , PrimApply(..)+ , PrimPatch, PrimPatchBase(..)+ , FromPrim(..), ToFromPrim(..)+ , PrimPatchCommon+ )+import qualified Darcs.Patch.Prim.V1 as Base ( Prim )++newtype Prim x y = Prim { unPrim :: Base.Prim x y } deriving+ ( Annotate+ , Commute+ , Invert+ , IsHunk+ , Eq2+ , PatchInspect+ , PrimApply+ , PrimCanonize+ , PrimClassify+ , PrimConstruct+ , PrimDetails+ , PrimPatchCommon+ )++instance PrimPatch Prim++instance Show (Prim wX wY) where+ showsPrec d (Prim p) =+ showParen (d > appPrec) $ showString "Prim " . showsPrec2 (appPrec + 1) p++instance Show1 (Prim wX) where+ showDict1 = ShowDictClass++instance Show2 Prim where+ showDict2 = ShowDictClass++instance PrimPatchBase Prim where+ type PrimOf Prim = Prim++instance FromPrim Prim where+ fromPrim = id++instance ToFromPrim Prim where+ toPrim = Just++instance ReadPatch Prim where+ readPatch' = do+ Sealed p <- readPrim OldFormat+ return (Sealed (Prim p))++fileNameFormat :: ShowPatchFor -> FileNameFormat+fileNameFormat ForDisplay = UserFormat+fileNameFormat ForStorage = OldFormat++instance ShowPatchBasic Prim where+ showPatch fmt = showPrim (fileNameFormat fmt) . unPrim++instance ShowContextPatch Prim where+ showContextPatch f = showPrimCtx (fileNameFormat f) . unPrim++instance ShowPatch Prim where+ summary = plainSummaryPrim . unPrim+ summaryFL = plainSummaryPrims False+ thing _ = "change"++instance PatchListFormat Prim where+ patchListFormat = ListFormatV1++instance Apply Prim where+ type ApplyState Prim = ApplyState Base.Prim+ apply = apply . unPrim++instance RepairToFL Prim where+ applyAndTryToFixFL = fmap coerce . applyAndTryToFixFL . unPrim
@@ -4,23 +4,28 @@ import Prelude () import Darcs.Prelude -import Darcs.Patch.Format ( FileNameFormat(..) )-import Darcs.Patch.Prim ( showPrim, PrimPatch )-+import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatchFor(..) ) import Darcs.Patch.V1.Core ( RepoPatchV1(..) ) -import Darcs.Util.Printer ( Doc,- text, blueText,- ($$), (<+>) )+import Darcs.Util.Printer ( Doc, text, blueText, ($$), (<+>) ) -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 -> RepoPatchV1 prim wA wB -> RepoPatchV1 prim wD wE -> Doc-showMerger merger_name p1 p2 =+showPatch_ :: ShowPatchBasic prim => prim wX wY -> Doc+showPatch_ = showPatch ForDisplay++showMerger :: ShowPatchBasic prim+ => ShowPatchFor+ -> String+ -> RepoPatchV1 prim wA wB+ -> RepoPatchV1 prim wD wE+ -> Doc+showMerger f merger_name p1 p2 = blueText merger_name <+> text "0.0" <+> blueText "("- $$ showPatch_ p1- $$ showPatch_ p2+ $$ showPatch f p1+ $$ showPatch f p2 $$ blueText ")"++instance ShowPatchBasic prim => ShowPatchBasic (RepoPatchV1 prim) where+ showPatch f (PP p) = showPatch f p+ showPatch f (Merger _ _ p1 p2) = showMerger f "merger" p1 p2+ showPatch f (Regrem _ _ p1 p2) = showMerger f "regrem" p1 p2
@@ -1,25 +1,19 @@ {-# 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.Show ( ShowPatch(..), ShowContextPatch(..), showPatch ) import Darcs.Patch.Summary ( plainSummary ) import Darcs.Patch.V1.Apply () import Darcs.Patch.V1.Core ( RepoPatchV1(..) )-import Darcs.Patch.V1.Show ( showPatch_ )-+import Darcs.Patch.V1.Show () -instance PrimPatch prim => ShowPatchBasic (RepoPatchV1 prim) where- showPatch = showPatch_+instance PrimPatch prim => ShowContextPatch (RepoPatchV1 prim) where+ showContextPatch f (PP p) = showContextPatch f p+ showContextPatch f p = return $ showPatch f p instance PrimPatch prim => ShowPatch (RepoPatchV1 prim) where- showContextPatch (PP p) = showContextPatch p- showContextPatch p = return $ showPatch p summary = plainSummary summaryFL = plainSummary thing _ = "change"-
@@ -1,11 +1,11 @@ {-# OPTIONS_GHC -fno-warn-orphans #-}-module Darcs.Patch.V2 ( RepoPatchV2, prim2repopatchV2 ) where+module Darcs.Patch.V2 ( RepoPatchV2 ) where +import Darcs.Patch.Annotate ( Annotate ) import Darcs.Patch.Matchable ( Matchable ) import Darcs.Patch.Prim ( PrimPatch ) import Darcs.Patch.RepoPatch ( RepoPatch )--import Darcs.Patch.V2.RepoPatch ( RepoPatchV2, prim2repopatchV2 )+import Darcs.Patch.V2.RepoPatch ( RepoPatchV2 ) instance PrimPatch prim => Matchable (RepoPatchV2 prim)-instance PrimPatch prim => RepoPatch (RepoPatchV2 prim)+instance (PrimPatch prim, Annotate prim) => RepoPatch (RepoPatchV2 prim)
@@ -16,7 +16,7 @@ -- Boston, MA 02110-1301, USA. {-# OPTIONS_GHC -fno-warn-orphans -fno-warn-name-shadowing #-}-{-# LANGUAGE CPP, FlexibleContexts, UndecidableInstances #-}+{-# LANGUAGE FlexibleContexts, UndecidableInstances #-} module Darcs.Patch.V2.Non@@ -44,18 +44,22 @@ import Data.List ( delete ) import Control.Monad ( liftM, mzero )+import Darcs.Patch.Apply ( Apply(..) ) import Darcs.Patch.Commute ( commuteFL ) import Darcs.Patch.Effect ( Effect(..) )-import Darcs.Patch.Format ( PatchListFormat, FileNameFormat(..) )+import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Invert ( Invert, invertFL, invertRL )-import Darcs.Patch.Prim ( FromPrim(..), ToFromPrim(..),- PrimOf, PrimPatchBase,- showPrim, sortCoalesceFL,- readPrim )-import Darcs.Patch.Patchy ( Patchy, showPatch, ReadPatch(..),- Commute(..), invert )+import Darcs.Patch.Prim+ ( FromPrim(..), ToFromPrim(..)+ , PrimOf, PrimPatchBase+ , sortCoalesceFL+ )+import Darcs.Patch.Commute ( Commute(..) )+import Darcs.Patch.Invert ( Invert(invert) )+import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.Show ( showPatch ) import Darcs.Patch.ReadMonads ( ParserM, lexChar )-import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..), EqCheck(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..), (+>+), mapRL_RL , (:>)(..), reverseFL, reverseRL )@@ -64,7 +68,7 @@ , showsPrec2 ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed) ) import Darcs.Patch.Read ( peekfor )-import Darcs.Patch.Show ( ShowPatchBasic )+import Darcs.Patch.Show ( ShowPatchBasic, ShowPatchFor ) import Darcs.Patch.Viewing () import Darcs.Patch.Permutations ( removeFL, commuteWhatWeCanFL ) import Darcs.Util.Printer ( Doc, empty, vcat, hiddenPrefix, blueText, ($$) )@@ -90,16 +94,18 @@ -- |showNons creates a Doc representing a list of Nons. showNons :: (ShowPatchBasic p, PatchListFormat p, PrimPatchBase p)- => [Non p wX] -> Doc-showNons [] = empty-showNons xs = blueText "{{" $$ vcat (map showNon xs) $$ blueText "}}"+ => ShowPatchFor -> [Non p wX] -> Doc+showNons _ [] = empty+showNons f xs = blueText "{{" $$ vcat (map (showNon f) xs) $$ blueText "}}" -- |showNon creates a Doc representing a Non.-showNon :: (ShowPatchBasic p, PatchListFormat p, PrimPatchBase p) => Non p wX+showNon :: (ShowPatchBasic p, PatchListFormat p, PrimPatchBase p)+ => ShowPatchFor+ -> Non p wX -> Doc-showNon (Non c p) = hiddenPrefix "|" (showPatch c)- $$ hiddenPrefix "|" (blueText ":")- $$ showPrim NewFormat p+showNon f (Non c p) = hiddenPrefix "|" (showPatch f c)+ $$ hiddenPrefix "|" (blueText ":")+ $$ showPatch f p -- |readNons is a parser that attempts to read a list of Nons. readNons :: (ReadPatch p, PatchListFormat p, PrimPatchBase p, ParserM m)@@ -108,20 +114,20 @@ where rns = peekfor (BC.pack "}}") (return []) $ do Sealed ps <- readPatch' lexChar ':'- Sealed p <- readPrim NewFormat+ Sealed p <- readPatch' (Non ps p :) `liftM` rns -- |readNon is a parser that attempts to read a single Non. readNon :: (ReadPatch p, PatchListFormat p, PrimPatchBase p, ParserM m) => m (Non p wX) readNon = do Sealed ps <- readPatch'- let doReadPrim = do Sealed p <- readPrim NewFormat+ let doReadPrim = do Sealed p <- readPatch' return $ Non ps p peekfor (BC.singleton ':') doReadPrim mzero -- |Nons are equal if their context patches are equal, and they have an equal -- prim patch.-instance (Commute p, MyEq p, MyEq (PrimOf p)) => Eq (Non p wX) where+instance (Commute p, Eq2 p, Eq2 (PrimOf p)) => Eq (Non p wX) where Non (cx :: FL p wX wY1) (x :: PrimOf p wY1 wZ1) == Non (cy :: FL p wX wY2) (y :: PrimOf p wY2 wZ2) = case cx =\/= cy of@@ -138,7 +144,7 @@ -- returns some variant @cy'@. If commutation suceeds, the variant is just -- straightforwardly the commuted version. If commutation fails, the variant -- consists of @x@ prepended to the context of @cy@.-commuteOrAddToCtx :: (Patchy p, ToFromPrim p) => p wX wY -> Non p wY+commuteOrAddToCtx :: (Commute p, ToFromPrim p) => p wX wY -> Non p wY -> Non p wX commuteOrAddToCtx p n | Just n' <- p >* n = n' commuteOrAddToCtx p (Non c x) = Non (p:>:c) x@@ -155,7 +161,7 @@ -- > x1 [c1'' c2'' y''] x2' x3' -- -- and return @[x1 c1'' c2'' y'']@-commuteOrAddToCtxRL :: (Patchy p, ToFromPrim p) => RL p wX wY -> Non p wY+commuteOrAddToCtxRL :: (Apply p, Commute p, Invert p, ToFromPrim p) => RL p wX wY -> Non p wY -> Non p wX commuteOrAddToCtxRL NilRL n = n commuteOrAddToCtxRL (ps:<:p) n = commuteOrAddToCtxRL ps $ commuteOrAddToCtx p n@@ -175,7 +181,7 @@ -- |commutePrimsOrAddToCtx takes a WL of prims and attempts to commute them -- past a Non.-commutePrimsOrAddToCtx :: (WL l, Patchy p, ToFromPrim p) => l (PrimOf p) wX wY+commutePrimsOrAddToCtx :: (WL l, Apply p, Commute p, Invert p, ToFromPrim p) => l (PrimOf p) wX wY -> Non p wY -> Non p wX commutePrimsOrAddToCtx q = commuteOrAddToCtxRL (mapRL_RL fromPrim $ toRL q) @@ -194,14 +200,14 @@ -- modify p2, so that it doesn't have any of a1' in its context. -- remNons really only works right if the relevant nons are conflicting...-remNons :: (Nonable p, Effect p, Patchy p, MyEq p, ToFromPrim p, PrimPatchBase p,- MyEq (PrimOf p)) => [Non p wX] -> Non p wX -> Non p wX+remNons :: (Nonable p, Effect p, Apply p, Commute p, Invert p, Eq2 p, ToFromPrim p, PrimPatchBase p)+ => [Non p wX] -> Non p wX -> Non p wX remNons ns n@(Non c x) = case remNonHelper ns c of NilFL :> c' -> Non c' x _ -> n where- remNonHelper :: (Nonable p, Effect p, Patchy p, MyEq p, ToFromPrim p,- PrimPatchBase p, MyEq (PrimOf p)) => [Non p wX]+ remNonHelper :: (Nonable p, Effect p, Apply p, Commute p, Invert p, Eq2 p, ToFromPrim p,+ PrimPatchBase p) => [Non p wX] -> FL p wX wY -> (FL (PrimOf p) :> FL p) wX wY remNonHelper [] x = NilFL :> x remNonHelper _ NilFL = NilFL :> NilFL@@ -223,26 +229,26 @@ -- -- TODO: understand if there is any case where p is equal to the prim patch of -- the Non, in which case, we return the original Non, is that right?-commuteOrRemFromCtx :: (Patchy p, MyEq p, ToFromPrim p) => p wX wY -> Non p wX+commuteOrRemFromCtx :: (Commute p, Invert p, Eq2 p, ToFromPrim p) => p wX wY -> Non p wX -> Maybe (Non p wY) commuteOrRemFromCtx p n | n'@(Just _) <- n *> p = n' commuteOrRemFromCtx p (Non pc x) = removeFL p pc >>= \c -> return (Non c x) -- |commuteOrRemFromCtxFL attempts to remove a FL of patches from a Non, -- returning Nothing if any of the individual removes fail.-commuteOrRemFromCtxFL :: (Patchy p, MyEq p, ToFromPrim p) => FL p wX wY -> Non p wX+commuteOrRemFromCtxFL :: (Apply p, Commute p, Invert p, Eq2 p, ToFromPrim p) => FL p wX wY -> Non p wX -> Maybe (Non p wY) commuteOrRemFromCtxFL NilFL n = Just n commuteOrRemFromCtxFL (p:>:ps) n = do n' <- commuteOrRemFromCtx p n commuteOrRemFromCtxFL ps n' -- |(*>) attemts to modify a Non by commuting it past a given patch.-(*>) :: (Patchy p, ToFromPrim p) => Non p wX -> p wX wY+(*>) :: (Commute p, Invert p, ToFromPrim p) => Non p wX -> p wX wY -> Maybe (Non p wY) n *> p = invert p >* n -- |(>*) attempts to modify a Non, by commuting a given patch past it.-(>*) :: (Patchy p, ToFromPrim p) => p wX wY -> Non p wY+(>*) :: (Commute p, ToFromPrim p) => p wX wY -> Non p wY -> Maybe (Non p wX) y >* (Non c x) = do c' :> y' <- commuteFL (y :> c)@@ -251,16 +257,16 @@ return (Non c' x') -- |(*>>) attempts to modify a Non by commuting it past a given WL of patches.-(*>>) :: (WL l, Patchy p, ToFromPrim p, PrimPatchBase p) => Non p wX+(*>>) :: (WL l, Apply p, Commute p, Invert p, ToFromPrim p, PrimPatchBase p) => Non p wX -> l (PrimOf p) wX wY -> Maybe (Non p wY) n *>> p = invertWL p >>* n -- |(>>*) attempts to modify a Non by commuting a given WL of patches past it.-(>>*) :: (WL l, Patchy p, ToFromPrim p) => l (PrimOf p) wX wY -> Non p wY+(>>*) :: (WL l, Apply p, Commute p, Invert p, ToFromPrim p) => l (PrimOf p) wX wY -> Non p wY -> Maybe (Non p wX) ps >>* n = commuteRLPastNon (toRL ps) n where- commuteRLPastNon :: (Patchy p, ToFromPrim p) => RL (PrimOf p) wX wY+ commuteRLPastNon :: (Apply p, Commute p, Invert p, ToFromPrim p) => RL (PrimOf p) wX wY -> Non p wY -> Maybe (Non p wX) commuteRLPastNon NilRL n = Just n commuteRLPastNon (xs:<:x) n = fromPrim x >* n >>= commuteRLPastNon xs
@@ -0,0 +1,120 @@+-- it is stupid that we need UndecidableInstances just to call another+-- type function (see instance Apply below which requires this)+{-# LANGUAGE UndecidableInstances #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+module Darcs.Patch.V2.Prim ( Prim(..) ) where++import Prelude ()+import Darcs.Prelude++import Data.Coerce (coerce )++import Darcs.Patch.Annotate ( Annotate )+import Darcs.Patch.Apply ( Apply(..) )+import Darcs.Patch.Commute ( Commute )+import Darcs.Patch.FileHunk ( IsHunk )+import Darcs.Patch.Format+ ( PatchListFormat(..)+ , ListFormat(ListFormatV2)+ , FileNameFormat(NewFormat,UserFormat) )+import Darcs.Patch.Inspect ( PatchInspect )+import Darcs.Patch.Invert ( Invert )+import Darcs.Patch.Read ( ReadPatch(..) )+import Darcs.Patch.Repair ( RepairToFL(..) )+import Darcs.Patch.Show+ ( ShowPatchBasic(..)+ , ShowPatchFor(..)+ , ShowPatch(..)+ , ShowContextPatch(..)+ )+import Darcs.Patch.Summary ( plainSummaryPrim, plainSummaryPrims )++import Darcs.Patch.Witnesses.Eq ( Eq2 )+import Darcs.Patch.Witnesses.Show+ ( Show1(..), Show2(..)+ , ShowDict(ShowDictClass)+ , appPrec, showsPrec2+ )+import Darcs.Patch.Witnesses.Sealed ( mapSeal )++import Darcs.Patch.Prim.Class+ ( PrimConstruct(..), PrimCanonize(..)+ , PrimClassify(..), PrimDetails(..)+ , PrimShow(..), PrimRead(..)+ , PrimApply(..)+ , PrimPatch, PrimPatchBase(..)+ , FromPrim(..), ToFromPrim(..)+ , PrimPatchCommon+ )+import qualified Darcs.Patch.Prim.V1 as Base ( Prim )++newtype Prim x y = Prim { unPrim :: Base.Prim x y } deriving+ ( Annotate+ , Commute+ , Invert+ , IsHunk+ , Eq2+ , PatchInspect+ , PrimApply+ , PrimCanonize+ , PrimClassify+ , PrimConstruct+ , PrimDetails+ , PrimPatchCommon+ )++instance PrimPatch Prim++instance Show (Prim wX wY) where+ showsPrec d (Prim p) =+ showParen (d > appPrec) $ showString "Prim " . showsPrec2 (appPrec + 1) p++instance Show1 (Prim wX) where+ showDict1 = ShowDictClass++instance Show2 Prim where+ showDict2 = ShowDictClass++instance PrimPatchBase Prim where+ type PrimOf Prim = Prim++instance FromPrim Prim where+ fromPrim = id++instance ToFromPrim Prim where+ toPrim = Just++instance ReadPatch Prim where+ readPatch' = fmap (mapSeal Prim) (readPrim NewFormat)++fileNameFormat :: ShowPatchFor -> FileNameFormat+fileNameFormat ForDisplay = UserFormat+fileNameFormat ForStorage = NewFormat++instance ShowPatchBasic Prim where+ showPatch f = showPrim (fileNameFormat f) . unPrim++instance ShowContextPatch Prim where+ showContextPatch f = showPrimCtx (fileNameFormat f) . unPrim++instance ShowPatch Prim where+ summary = plainSummaryPrim . unPrim+ summaryFL = plainSummaryPrims False+ thing _ = "change"++-- This instance is here so that FL Prim and RL Prim also get+-- ShowPatch instances, see Darcs.Patch.Viewing+instance PatchListFormat 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++instance Apply Prim where+ type ApplyState Prim = ApplyState Base.Prim+ apply = apply . unPrim++instance RepairToFL Prim where+ applyAndTryToFixFL = fmap coerce . applyAndTryToFixFL . unPrim
@@ -16,12 +16,9 @@ -- 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@@ -35,43 +32,45 @@ import qualified Data.ByteString.Char8 as BC ( ByteString, pack ) import Data.Maybe ( fromMaybe ) import Data.List ( partition, nub )+import Data.List.Ordered ( nubSort ) +import Darcs.Patch.Annotate ( Annotate(..) ) import Darcs.Patch.Commute ( commuteFL, commuteFLorComplain, commuteRL- , commuteRLFL )+ , commuteRLFL, Commute(..) ) 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.Format ( PatchListFormat(..), ListFormat(ListFormatV2) )+import Darcs.Patch.Invert ( invertFL, invertRL, Invert(..) )+import Darcs.Patch.Merge ( Merge(..), naturalMerge )+import Darcs.Patch.Prim ( FromPrim(..), ToFromPrim(..)+ , PrimPatchBase(..), PrimPatch )+import Darcs.Patch.Read ( bracketedFL, ReadPatch(..) ) 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.Apply ( Apply(..) )+import Darcs.Patch.Inspect ( PatchInspect(..) ) import Darcs.Patch.Permutations ( commuteWhatWeCanFL, commuteWhatWeCanRL , genCommuteWhatWeCanRL, removeRL, removeFL , removeSubsequenceFL )-import Darcs.Patch.Show ( ShowPatchBasic(..) )+import Darcs.Patch.Show+ ( ShowPatch(..), ShowPatchBasic(..), ShowContextPatch(..), ShowPatchFor(..)+ , displayPatch ) 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.Eq ( Eq2(..), EqCheck(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), RL(..), (:>)(..), (+>+), (+<+)- , mapFL_FL, reverseFL, (:\/:)(..), (:/\:)(..)- , reverseRL, lengthFL, lengthRL, nullFL )+ , mapFL, mapFL_FL, reverseFL, (:\/:)(..), (:/\:)(..)+ , reverseRL, lengthFL, lengthRL, nullFL, initsFL ) import Darcs.Patch.Witnesses.Sealed ( FlippedSeal(..), Sealed(Sealed), mapSeal , unseal )@@ -79,11 +78,9 @@ ( Show1(..), Show2(..), ShowDict(..) , showsPrec2, appPrec )-import Darcs.Util.Printer.Color ( errorDoc, assertDoc ) -import Darcs.Util.Printer ( Doc, blueText, redText, (<+>), ($$) )--#include "impossible.h"+import Darcs.Util.Printer.Color ( errorDoc, assertDoc )+import Darcs.Util.Printer ( Doc, blueText, redText, (<+>), ($$), vcat ) -- |'RepoPatchV2' is used to represents prim patches that are duplicates of, or -- conflict with, another prim patch in the repository.@@ -141,7 +138,7 @@ p@(Etacilpud _) -> justRedP "An inverse duplicate" p _ -> Nothing where- justRedP msg p = Just $ redText msg $$ showPatch p+ justRedP msg p = Just $ redText msg $$ displayPatch p -- |'mergeUnravelled' is used when converting from Darcs V1 patches (Mergers) -- to Darcs V2 patches (Conflictors).@@ -155,7 +152,7 @@ Just NilRL -> bug "found no patches in mergeUnravelled" Just (_ :<: z) -> Just $ FlippedSeal z where- notNullS :: PrimPatch prim => Sealed ((FL prim) wX) -> Bool+ notNullS :: Sealed ((FL prim) wX) -> Bool notNullS (Sealed NilFL) = False notNullS _ = True @@ -191,7 +188,7 @@ 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+ Commute (PrimOf p), Eq2 (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@@ -204,7 +201,7 @@ -> RepoPatchV2 prim wX wY assertConsistent x = flip assertDoc x $ do e <- isConsistent x- Just (redText "Inconsistent patch:" $$ showPatch x $$ e)+ Just (redText "Inconsistent patch:" $$ displayPatch x $$ e) -- | @mergeAfterConflicting@ takes as input a sequence of conflicting patches -- @xxx@ (which therefore have no effect) and a sequence of primitive patches@@ -267,7 +264,7 @@ case mergeConflictingNons ix of Nothing -> errorDoc $ redText "mergeConflictingNons failed in geteff: ix" $$- showNons ix $$ redText "xx" $$ showPatch xx+ displayNons ix $$ redText "xx" $$ displayPatch xx Just rix -> case mergeAfterConflicting rix xx of Just (a, x) ->@@ -276,9 +273,9 @@ Nothing -> errorDoc $ redText "mergeAfterConflicting failed in geteff" $$- redText "where ix" $$ showNons ix $$- redText "and xx" $$ showPatch xx $$- redText "and rix" $$ showPatch rix+ redText "where ix" $$ displayNons ix $$+ redText "and xx" $$ displayPatch xx $$+ redText "and rix" $$ displayPatch rix xx2nons :: PrimPatch prim => [Non (RepoPatchV2 prim) wX] -> FL prim wX wY -> [Non (RepoPatchV2 prim) wX]@@ -309,18 +306,18 @@ 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+ displayNons nmm | Nothing <- (nmm ++ im) `minus` toNons deps = Just $ redText "dependencies not in conflict:" $$- showNons (toNons deps) $$+ displayNons (toNons deps) $$ redText "compared with deps itself:" $$- showPatch deps+ displayPatch 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+ displayNons imnc where (nmm, rmm) = geteff im mm everyoneConflicts :: PrimPatch prim => [Non (RepoPatchV2 prim) wX] -> Bool@@ -329,10 +326,6 @@ ([], _) -> 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@@ -470,13 +463,13 @@ isInconsistent = isConsistent instance FromPrim (RepoPatchV2 prim) where- fromPrim = prim2repopatchV2+ fromPrim = Normal instance ToFromPrim (RepoPatchV2 prim) where toPrim (Normal p) = Just p toPrim _ = Nothing -instance PrimPatch prim => MyEq (RepoPatchV2 prim) where+instance PrimPatch prim => Eq2 (RepoPatchV2 prim) where (Duplicate x) =\/= (Duplicate y) | x == y = IsEq (Etacilpud x) =\/= (Etacilpud y) | x == y = IsEq (Normal x) =\/= (Normal y) = x =\/= y@@ -519,8 +512,8 @@ -> 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)+toNons :: forall p wX wY . (Commute p, PatchListFormat 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@@ -533,9 +526,9 @@ 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+ z :> zs -> displayPatch (z :>: zs)) $$+ redText "ds are" $$ displayPatch ds $$+ redText "pp is" $$ displayPatch pp reverseFoo :: (p :> FL p) wX wZ -> (RL p :> p) wX wZ reverseFoo (p :> ps) = rf NilRL p ps@@ -548,12 +541,6 @@ 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@@ -651,17 +638,11 @@ -- 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')+ -- First try the natural (non-conflicting) merge.+ | Just (y' :/\: x') <-+ naturalMerge ((assertConsistent x) :\/: (assertConsistent y))+ = assertConsistent y' :/\: assertConsistent x' -- If we detect equal patches, we have a duplicate. | IsEq <- x =\/= y , n <- commuteOrAddToCtx (invert x) $ non x =@@ -768,12 +749,12 @@ (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 :: Merge p => (p :\/: p) wX wY+ -> (p :/\: p) 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 :: (Invert p, Commute p) => (p :> p) wX wY+ -> Maybe ((p :> p) wX wY) invertCommute (x :> y) = do ix' :> iy' <- commute (invert y :> invert x) return (invert iy' :> invert ix') @@ -785,7 +766,7 @@ -- | '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 :: (Commute p, Eq2 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 =@@ -803,7 +784,7 @@ -- | '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 :: (Commute p, Eq2 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 =@@ -829,6 +810,9 @@ mapMaybeSnd (mapFL_FL Normal) `liftM` applyAndTryToFixFL p applyAndTryToFixFL x = do apply x; return Nothing +instance (PrimPatch prim, Annotate prim) => Annotate (RepoPatchV2 prim) where+ annotate = annotate . effect+ 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@@ -843,27 +827,29 @@ 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 $$+ showPatch f (Duplicate d) = blueText duplicate $$ showNon f d+ showPatch f (Etacilpud d) = blueText etacilpud $$ showNon f d+ showPatch f (Normal p) = showPatch f p+ showPatch f (Conflictor i NilFL p) =+ blueText conflictor <+> showNons f i <+> blueText "[]" $$ showNon f p+ showPatch f (Conflictor i cs p) =+ blueText conflictor <+> showNons f i <+> blueText "[" $$+ showFL f 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 $$+ showNon f p+ showPatch f (InvConflictor i NilFL p) =+ blueText rotcilfnoc <+> showNons f i <+> blueText "[]" $$ showNon f p+ showPatch f (InvConflictor i cs p) =+ blueText rotcilfnoc <+> showNons f i <+> blueText "[" $$+ showFL f cs $$ blueText "]" $$- showNon p+ showNon f p +instance PrimPatch prim => ShowContextPatch (RepoPatchV2 prim) where+ showContextPatch f (Normal p) = showContextPatch f p+ showContextPatch f p = return $ showPatch f p+ instance PrimPatch prim => ShowPatch (RepoPatchV2 prim) where- showContextPatch (Normal p) = showContextPatch p- showContextPatch c = return $ showPatch c summary = plainSummary summaryFL = plainSummary thing _ = "change"@@ -874,7 +860,7 @@ let str = string . BC.pack readConflictorPs = do i <- readNons- ps <- bracketedFL (readPrim NewFormat) '[' ']'+ ps <- bracketedFL readPatch' '[' ']' p <- readNon return (i, ps, p) choice [ do str duplicate@@ -889,29 +875,29 @@ , do str rotcilfnoc (i, Sealed ps, p) <- readConflictorPs return $ Sealed $ InvConflictor i ps p- , do Sealed p <- readPrim NewFormat+ , do Sealed p <- readPatch' 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+ showParen (d > appPrec) $ showString "Normal " . showsPrec2 (appPrec + 1) prim showsPrec d (Duplicate x) =- showParen (d > appPrec) $ showString "Darcs.Patch.V2.RepoPatch.Duplicate " . showsPrec (appPrec + 1) x+ showParen (d > appPrec) $ showString "Duplicate " . showsPrec (appPrec + 1) x showsPrec d (Etacilpud x) =- showParen (d > appPrec) $ showString "Darcs.Patch.V2.Etacilpud " . showsPrec (appPrec + 1) x+ showParen (d > appPrec) $ showString "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 "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 "InvConflictor " . showsPrec (appPrec + 1) ix . showString " " . showsPrec (appPrec + 1) xx . showString " " . showsPrec (appPrec + 1) x @@ -943,3 +929,10 @@ instance IsHunk prim => IsHunk (RepoPatchV2 prim) where isHunk rp = do Normal p <- return rp isHunk p++displayNons :: (PatchListFormat p, ShowPatchBasic p, PrimPatchBase p) =>+ [Non p wX] -> Doc+displayNons p = showNons ForDisplay p++showFL :: ShowPatchBasic p => ShowPatchFor -> FL p wX wY -> Doc+showFL f = vcat . mapFL (showPatch f)
@@ -16,18 +16,16 @@ -- Boston, MA 02110-1301, USA. {-# OPTIONS_GHC -fno-warn-orphans -fno-warn-unused-imports #-}-{-# LANGUAGE CPP #-} module Darcs.Patch.Viewing ( showContextHunk- , showContextSeries ) where import Prelude () import Darcs.Prelude import Control.Applicative( (<$>) )-import qualified Data.ByteString as BS ( null )+import qualified Data.ByteString as B ( null ) import Prelude hiding ( pi, readFile ) import Darcs.Util.Tree ( Tree ) import Darcs.Util.Tree.Monad ( virtualTreeMonad )@@ -38,24 +36,26 @@ import Darcs.Patch.FileHunk ( IsHunk(..), FileHunk(..), showFileHunk ) import Darcs.Patch.Format ( PatchListFormat(..), ListFormat(..), FileNameFormat(..) )-import Darcs.Patch.Show ( ShowPatchBasic(..), ShowPatch(..), formatFileName )+import Darcs.Patch.Show+ ( ShowPatchBasic(..), ShowPatch(..)+ , formatFileName, ShowPatchFor(..), ShowContextPatch(..) ) import Darcs.Patch.Witnesses.Ordered ( RL(..), FL(..), mapFL, mapFL_FL, reverseRL, concatFL ) import Darcs.Util.ByteString ( linesPS ) import Darcs.Util.Printer ( Doc, empty, vcat, text, blueText, Color(Cyan, Magenta), lineColor, ($$), (<+>), prefix, userchunkPS ) -showContextSeries :: forall p m wX wY . (Apply p, ShowPatch p, IsHunk p,+showContextSeries :: forall p m wX wY . (Apply p, ShowContextPatch p, IsHunk p, ApplyMonad (ApplyState p) m)- => FL p wX wY -> m Doc-showContextSeries = scs Nothing+ => ShowPatchFor -> FileNameFormat -> FL p wX wY -> m Doc+showContextSeries use fmt = scs Nothing where 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 Nothing -> do- a <- showContextPatch p+ a <- showContextPatch use p b <- nestedApply (scs Nothing ps) s' return $ a $$ fst b Just fh -> case ps of@@ -69,29 +69,30 @@ cool :: Maybe (FileHunk wA wB) -> FileHunk wB wC -> Maybe (FileHunk wC wD) -> (ApplyState p) (ApplyMonadBase m) -> (ApplyMonadBase m) Doc cool pold fh ps s =- fst <$> virtualTreeMonad (coolContextHunk pold fh ps) (toTree s)+ fst <$> virtualTreeMonad (coolContextHunk fmt pold fh ps) (toTree s) -showContextHunk :: (ApplyMonad Tree m) => FileHunk wX wY -> m Doc-showContextHunk h = coolContextHunk Nothing h Nothing+showContextHunk :: (ApplyMonad Tree m) => FileNameFormat -> FileHunk wX wY -> m Doc+showContextHunk fmt h = coolContextHunk fmt Nothing h Nothing coolContextHunk :: (ApplyMonad Tree m)- => Maybe (FileHunk wA wB) -> FileHunk wB wC+ => FileNameFormat+ -> Maybe (FileHunk wA wB) -> FileHunk wB wC -> Maybe (FileHunk wC wD) -> m Doc-coolContextHunk prev fh@(FileHunk f l o n) next = do+coolContextHunk fmt prev fh@(FileHunk f l o n) next = do have <- mDoesFileExist f content <- if have then Just `fmap` mReadFilePS f else return Nothing case linesPS `fmap` content of- -- This is a weird error...- Nothing -> return $ showFileHunk OldFormat fh+ -- FIXME This is a weird error...+ Nothing -> return $ showFileHunk fmt fh Just ls -> let pre = take numpre $ drop (l - numpre - 1) ls cleanedls = case reverse ls of (x : xs)- | BS.null x -> reverse xs+ | B.null x -> reverse xs _ -> ls post = take numpost $ drop (max 0 $ l+length o-1) cleanedls in return $- blueText "hunk" <+> formatFileName OldFormat f+ blueText "hunk" <+> formatFileName fmt f <+> text (show l) $$ prefix " " (vcat $ map userchunkPS pre) $$ lineColor Magenta (prefix "-" (vcat $ map userchunkPS o))@@ -111,33 +112,36 @@ _ -> 3 instance (PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (FL p) where- showPatch = showPatchInternal patchListFormat+ showPatch ForDisplay = vcat . mapFL (showPatch ForDisplay)+ showPatch ForStorage = showPatchInternal patchListFormat where showPatchInternal :: ListFormat p -> FL p wX wY -> Doc- showPatchInternal ListFormatV1 (p :>: NilFL) = showPatch p+ showPatchInternal ListFormatV1 (p :>: NilFL) = (showPatch ForStorage) p showPatchInternal ListFormatV1 NilFL = blueText "{" $$ blueText "}" showPatchInternal ListFormatV1 ps = blueText "{"- $$ vcat (mapFL showPatch ps)+ $$ vcat (mapFL (showPatch ForStorage) ps) $$ blueText "}"- showPatchInternal ListFormatV2 ps = vcat (mapFL showPatch ps)- showPatchInternal ListFormatDefault ps = vcat (mapFL showPatch ps)+ showPatchInternal ListFormatV2 ps = vcat (mapFL (showPatch ForStorage) ps)+ showPatchInternal ListFormatDefault ps = vcat (mapFL (showPatch ForStorage) ps) -instance (Apply p, IsHunk p, PatchListFormat p, ShowPatch p)- => ShowPatch (FL p) where- showContextPatch = showContextPatchInternal patchListFormat+instance (Apply p, IsHunk p, PatchListFormat p, ShowContextPatch p)+ => ShowContextPatch (FL p) where+ showContextPatch ForDisplay = showContextSeries ForDisplay UserFormat+ showContextPatch ForStorage = showContextPatchInternal patchListFormat where showContextPatchInternal :: (ApplyMonad (ApplyState (FL p)) m) => ListFormat p -> FL p wX wY -> m Doc showContextPatchInternal ListFormatV1 (p :>: NilFL) =- showContextPatch p+ showContextPatch ForStorage p showContextPatchInternal ListFormatV1 NilFL = return $ blueText "{" $$ blueText "}" showContextPatchInternal ListFormatV1 ps = do- x <- showContextSeries ps+ x <- showContextSeries ForStorage OldFormat ps return $ blueText "{" $$ x $$ blueText "}"- showContextPatchInternal ListFormatV2 ps = showContextSeries ps- showContextPatchInternal ListFormatDefault ps = showContextSeries ps+ showContextPatchInternal ListFormatV2 ps = showContextSeries ForStorage NewFormat ps+ showContextPatchInternal ListFormatDefault ps = showContextSeries ForStorage NewFormat ps +instance (PatchListFormat p, ShowPatch p) => ShowPatch (FL p) where description = vcat . mapFL description summary = summaryFL@@ -152,12 +156,13 @@ things = thing instance (PatchListFormat p, ShowPatchBasic p) => ShowPatchBasic (RL p) where- showPatch = showPatch . reverseRL+ showPatch f = showPatch f . reverseRL -instance (Apply p, IsHunk p, PatchListFormat p, ShowPatch p)- => ShowPatch (RL p) where- showContextPatch = showContextPatch . reverseRL+instance (ShowContextPatch p, Apply p, IsHunk p, PatchListFormat p)+ => ShowContextPatch (RL p) where+ showContextPatch use = showContextPatch use . reverseRL +instance (PatchListFormat p, ShowPatch p) => ShowPatch (RL p) where description = description . reverseRL summary = summary . reverseRL
@@ -1,6 +1,6 @@ module Darcs.Patch.Witnesses.Eq ( EqCheck(..)- , MyEq(..)+ , Eq2(..) , isIsEq ) where @@ -26,7 +26,7 @@ -- |An witness aware equality class. -- A minimal definition defines any one of 'unsafeCompare', '=\/=' and '=/\='.-class MyEq p where+class Eq2 p where -- |It is unsafe to define a class instance via this method, because -- if it returns True then the default implementations of '=\/=' and '=/\=' -- will coerce the equality of two witnesses.
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.Patch.Witnesses.Ordered ( -- * Directed Types@@ -30,50 +28,58 @@ , (:/\:)(..) , (:||:)(..) , Fork(..)- -- * Functions+ -- * Functions for 'FL's and 'RL's+ , nullFL+ , nullRL , lengthFL+ , lengthRL , mapFL+ , mapRL , mapFL_FL- , spanFL+ , mapRL_RL , foldlFL+ , foldlRL , allFL+ , allRL , anyFL+ , anyRL , filterFL+ , filterRL , splitAtFL , splitAtRL- , bunchFL- , foldlRL- , lengthRL- , isShorterThanRL- , mapRL- , mapRL_RL- , zipWithFL , filterOutFLFL , filterOutRLRL- , filterRL , reverseFL , reverseRL , (+>+) , (+<+)- , nullFL+ , (+>>+)+ , (+<<+) , concatFL , concatRL- , snocRLSealed- , nullRL- , toFL , dropWhileFL , dropWhileRL+ -- * 'FL' only+ , bunchFL+ , foldFL_M+ , spanFL , spanFL_M+ , zipWithFL+ , toFL , mapFL_FL_M+ , sequenceFL_ , eqFL , eqFLRev , eqFLUnsafe+ , initsFL+ -- * 'RL' only+ , isShorterThanRL+ , snocRLSealed ) where import Prelude () import Darcs.Prelude -#include "impossible.h" import Darcs.Patch.Witnesses.Show import Darcs.Patch.Witnesses.Sealed ( FlippedSeal(..)@@ -84,7 +90,7 @@ , Sealed2(..) , seal )-import Darcs.Patch.Witnesses.Eq ( MyEq(..), EqCheck(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2(..), EqCheck(..) ) -- * Directed Types @@ -221,11 +227,11 @@ instance (Show2 a, Show2 b) => Show ( (a :> b) wX wY ) where showsPrec d (x :> y) = showOp2 1 ":>" d x y -instance (MyEq a, MyEq b) => MyEq (a :> b) where+instance (Eq2 a, Eq2 b) => Eq2 (a :> b) where (a1 :> b1) =\/= (a2 :> b2) | IsEq <- a1 =\/= a2 = b1 =\/= b2 | otherwise = NotEq -instance (MyEq a, MyEq b) => Eq ((a :> b) wX wY) where+instance (Eq2 a, Eq2 b) => Eq ((a :> b) wX wY) where (==) = unsafeCompare instance (Show2 a, Show2 b) => Show2 (a :> b) where@@ -262,7 +268,7 @@ filterOutFLFL :: (forall wX wY . p wX wY -> EqCheck wX wY) -> FL p wW wZ -> FL p wW wZ filterOutFLFL _ NilFL = NilFL filterOutFLFL f (x:>:xs) | IsEq <- f x = filterOutFLFL f xs- | otherwise = x :>: filterOutFLFL f xs+ | otherwise = x :>: filterOutFLFL f xs filterOutRLRL :: (forall wX wY . p wX wY -> EqCheck wX wY) -> RL p wW wZ -> RL p wW wZ filterOutRLRL _ NilRL = NilRL@@ -344,6 +350,14 @@ bFL bs = case splitAtFL n bs of a :> b -> a :>: bFL b +-- | Monadic fold over an 'FL'+-- associating to the left, i.e. from left to right.+-- The order of arguments follows the standard 'foldM' from base.+foldFL_M :: Monad m+ => (forall wA wB. r wA -> p wA wB -> m (r wB))+ -> r wX -> FL p wX wY -> m (r wY)+foldFL_M _ r NilFL = return r+foldFL_M f r (x :>: xs) = f r x >>= \r' -> foldFL_M f r' xs allFL :: (forall wX wY . a wX wY -> Bool) -> FL a wW wZ -> Bool allFL f xs = and $ mapFL f xs@@ -351,6 +365,12 @@ anyFL :: (forall wX wY . a wX wY -> Bool) -> FL a wW wZ -> Bool anyFL f xs = or $ mapFL f xs +allRL :: (forall wA wB . a wA wB -> Bool) -> RL a wX wY -> Bool+allRL f xs = and $ mapRL f xs++anyRL :: (forall wA wB . a wA wB -> Bool) -> RL a wX wY -> Bool+anyRL f xs = or $ mapRL f xs+ foldlFL :: (forall wW wY . a -> b wW wY -> a) -> a -> FL b wX wZ -> a foldlFL _ x NilFL = x foldlFL f x (y:>:ys) = foldlFL f (f x y) ys@@ -369,6 +389,9 @@ bs <- mapFL_FL_M f as return (b:>:bs) +sequenceFL_ :: Monad m => (forall wW wZ . a wW wZ -> m b) -> FL a wX wY -> m ()+sequenceFL_ f = sequence_ . mapFL f+ zipWithFL :: (forall wX wY . a -> p wX wY -> q wX wY) -> [a] -> FL p wW wZ -> FL q wW wZ zipWithFL f (x:xs) (y :>: ys) = f x y :>: zipWithFL f xs ys@@ -430,19 +453,38 @@ | otherwise = seal xs -- |Check that two 'FL's are equal element by element.--- This differs from the 'MyEq' instance for 'FL' which+-- This differs from the 'Eq2' instance for 'FL' which -- uses commutation.-eqFL :: MyEq a => FL a wX wY -> FL a wX wZ -> EqCheck wY wZ+eqFL :: Eq2 a => FL a wX wY -> FL a wX wZ -> EqCheck wY wZ eqFL NilFL NilFL = IsEq eqFL (x:>:xs) (y:>:ys) | IsEq <- x =\/= y, IsEq <- eqFL xs ys = IsEq eqFL _ _ = NotEq -eqFLRev :: MyEq a => FL a wX wZ -> FL a wY wZ -> EqCheck wX wY+eqFLRev :: Eq2 a => FL a wX wZ -> FL a wY wZ -> EqCheck wX wY eqFLRev NilFL NilFL = IsEq eqFLRev (x:>:xs) (y:>:ys) | IsEq <- eqFLRev xs ys, IsEq <- x =/\= y = IsEq eqFLRev _ _ = NotEq -eqFLUnsafe :: MyEq a => FL a wX wY -> FL a wZ wW -> Bool+eqFLUnsafe :: Eq2 a => FL a wX wY -> FL a wZ wW -> Bool eqFLUnsafe NilFL NilFL = True eqFLUnsafe (x:>:xs) (y:>:ys) = unsafeCompare x y && eqFLUnsafe xs ys eqFLUnsafe _ _ = False++infixr 5 +>>++infixl 5 +<<+++-- | Prepend an 'RL' to an 'FL'. This traverses only the left hand side.+(+>>+) :: RL p wX wY -> FL p wY wZ -> FL p wX wZ+NilRL +>>+ ys = ys+(xs:<:x) +>>+ ys = xs +>>+ (x :>: ys)++-- | Append an 'FL' to an 'RL'. This traverses only the right hand side.+(+<<+) :: RL p wX wY -> FL p wY wZ -> RL p wX wZ+xs +<<+ NilFL = xs+xs +<<+ (y:>:ys) = (xs:<:y) +<<+ ys++initsFL :: 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)
@@ -15,7 +15,7 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, FlexibleInstances #-}+{-# LANGUAGE FlexibleInstances #-} {-# OPTIONS_HADDOCK ignore-exports #-} module Darcs.Patch.Witnesses.Sealed@@ -46,7 +46,7 @@ import Prelude () import Darcs.Prelude -import Darcs.Patch.Witnesses.Eq ( MyEq, EqCheck(..) )+import Darcs.Patch.Witnesses.Eq ( Eq2, EqCheck(..) ) import Darcs.Patch.Witnesses.Show import Darcs.Patch.Witnesses.Eq ( (=\/=) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP1, unsafeCoerceP )@@ -60,7 +60,7 @@ seal :: a wX -> Sealed a seal = Sealed -instance MyEq a => Eq (Sealed (a wX)) where+instance Eq2 a => Eq (Sealed (a wX)) where Sealed x == Sealed y | IsEq <- x =\/= y = True | otherwise = False @@ -139,7 +139,7 @@ -- of the type witnesses. newtype FreeLeft p = FLInternal (Poly (Stepped Sealed p)) --- |'FreeLeft' p is @ \forall y . \exists x . p x y @+-- |'FreeRight' p is @ \forall y . \exists x . p x y @ -- In other words the caller is free to specify the right witness, -- and then the left witness is an existential. -- Note that the order of the type constructors is important for ensuring
@@ -1,5 +1,3 @@-{-# LANGUAGE CPP #-}- module Darcs.Patch.Witnesses.Show ( ShowDict(..) , showD
@@ -1,4 +1,3 @@-{-# LANGUAGE MagicHash #-} module Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP , unsafeCoercePStart@@ -7,19 +6,19 @@ , unsafeCoerceP1 ) where -import GHC.Base (unsafeCoerce#)+import Unsafe.Coerce unsafeCoerceP :: a wX wY -> a wB wC-unsafeCoerceP = unsafeCoerce#+unsafeCoerceP = unsafeCoerce unsafeCoercePStart :: a wX1 wY -> a wX2 wY-unsafeCoercePStart = unsafeCoerce#+unsafeCoercePStart = unsafeCoerce unsafeCoercePEnd :: a wX wY1 -> a wX wY2-unsafeCoercePEnd = unsafeCoerce#+unsafeCoercePEnd = unsafeCoerce unsafeCoerceP2 :: t wW wX wY wZ -> t wA wB wC wD-unsafeCoerceP2 = unsafeCoerce#+unsafeCoerceP2 = unsafeCoerce unsafeCoerceP1 :: a wX -> a wY-unsafeCoerceP1 = unsafeCoerce#+unsafeCoerceP1 = unsafeCoerce
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.Patch.Witnesses.WZipper ( FZipper(..) , focus
@@ -2,27 +2,32 @@ -- 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.+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+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.+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.Semigroup , module Data.Traversable+ , module Data.Maybe+ , impossible, bug ) where import Prelude hiding@@ -30,12 +35,16 @@ -- 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+ -- 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.Semigroup in older GHCs+ Semigroup(..)+ , -- because it's in the new Prelude but only in Data.Traversable in older GHCs traverse ,@@ -51,4 +60,13 @@ import Control.Applicative ( Applicative(..), (<$>), (<*>) ) import Data.Monoid ( Monoid(..) )+import Data.Semigroup ( Semigroup(..) ) import Data.Traversable ( traverse )+import Data.Maybe ( fromJust )++impossible :: a+impossible = error "Impossible case"++bug :: String -> a+bug str = error ("This is a bug! Please report it at http://darcs.net\n" ++ str)+
@@ -16,9 +16,13 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, ScopedTypeVariables #-} module Darcs.Repository ( Repository+ , repoLocation+ , repoFormat+ , repoPristineType+ , repoCache+ , PristineType(..) , HashedDir(..) , Cache(..) , CacheLoc(..)@@ -30,7 +34,8 @@ , withRepoLock , withRepoLockCanFail , withRepository- , withRepositoryDirectory+ , withRepositoryLocation+ , withUMaskFlag , writePatchSet , findRepository , amInRepository@@ -40,7 +45,6 @@ , readRepo , prefsUrl , repoPatchType- , readRepoUsingSpecificInventory , addToPending , addPendingDiffToPending , tentativelyAddPatch@@ -54,8 +58,10 @@ , revertRepositoryChanges , finalizeRepositoryChanges , createRepository+ , createRepositoryV1+ , createRepositoryV2+ , EmptyRepository(..) , cloneRepository- , patchSetToRepository , unrevertUrl , applyToWorking , createPristineDirectoryTree@@ -67,7 +73,6 @@ , PatchInfoAnd , setScriptsExecutable , setScriptsExecutablePatches- , checkUnrelatedRepos , testTentative , modifyCache , reportBadSources@@ -75,77 +80,66 @@ , readRecorded , readUnrecorded , unrecordedChanges- , unrecordedChangesWithPatches , filterOutConflicts , readPending , readRecordedAndPending -- * Index. , readIndex , invalidateIndex- -- * Used as command arguments- , listFiles- , listRegisteredFiles- , listUnregisteredFiles ) where -import Prelude ()-import Darcs.Prelude--import Control.Monad ( unless, when )-import Data.List ( (\\) )-import System.Exit ( exitSuccess )- import Darcs.Repository.State ( readRecorded , readUnrecorded , unrecordedChanges- , unrecordedChangesWithPatches- , readPendingAndWorking , readPending , readIndex , invalidateIndex , readRecordedAndPending- , restrictDarcsdir- , restrictBoring- , applyTreeFilter , filterOutConflicts+ , addPendingDiffToPending+ , addToPending ) -import Darcs.Repository.Internal- ( Repository(..)- , maybeIdentifyRepository+import Darcs.Repository.Prefs ( prefsUrl )++import Darcs.Repository.Identify+ ( maybeIdentifyRepository , identifyRepositoryFor , findRepository , amInRepository , amNotInRepository , amInHashedRepository- , readRepo+ )+import Darcs.Repository.Hashed+ ( readRepo , readTentativeRepo- , readRepoUsingSpecificInventory- , prefsUrl , withRecorded , tentativelyAddPatch , tentativelyRemovePatches- , tentativelyAddToPending , revertRepositoryChanges , finalizeRepositoryChanges , unrevertUrl- , applyToWorking , createPristineDirectoryTree , createPartialsPristineDirectoryTree , reorderInventory , cleanRepository+ )+import Darcs.Repository.Pending+ ( tentativelyAddToPending+ )+import Darcs.Repository.Working+ ( applyToWorking , setScriptsExecutable , setScriptsExecutablePatches- , makeNewPending- , repoPatchType ) import Darcs.Repository.Job ( RepoJob(..) , withRepoLock , withRepoLockCanFail , withRepository- , withRepositoryDirectory+ , withRepositoryLocation+ , withUMaskFlag ) import Darcs.Repository.Rebase ( RebaseJobFlags(..), withManualRebaseUpdate ) import Darcs.Repository.Test ( testTentative )@@ -158,111 +152,27 @@ , WritableOrNot(..) , reportBadSources )-import Darcs.Repository.InternalTypes ( modifyCache )-import Darcs.Repository.Flags- ( DiffAlgorithm (..)- , ScanKnown(..)- , UpdateWorking(..)- , UseCache(..)- , UseIndex(..)+import Darcs.Repository.InternalTypes+ ( Repository+ , PristineType(..)+ , modifyCache+ , repoPatchType+ , repoLocation+ , repoFormat+ , repoPristineType+ , repoCache ) import Darcs.Repository.Clone- ( createRepository- , cloneRepository+ ( cloneRepository , replacePristine , writePatchSet- , patchSetToRepository )+import Darcs.Repository.Create+ ( createRepository+ , createRepositoryV1+ , createRepositoryV2+ , EmptyRepository(..)+ ) -import Darcs.Patch ( RepoPatch- , PrimOf- )-import Darcs.Patch.Set ( PatchSet(..)- , SealedPatchSet- )-import Darcs.Patch.Commute( commuteFL )-import Darcs.Patch.Permutations ( genCommuteWhatWeCanRL )+import Darcs.Patch.Set ( PatchSet(..), SealedPatchSet ) 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- , FL(..)- , (+>+)- )-import Darcs.Patch.Depends ( areUnrelatedRepos )--import Darcs.Util.Prompt ( promptYorn )-import Darcs.Util.Path( anchorPath )--import Darcs.Util.Tree( Tree, emptyTree, expand, list )-import Darcs.Util.Tree.Plain( readPlainTree )--checkUnrelatedRepos :: RepoPatch p- => Bool- -> PatchSet rt p wStart wX- -> PatchSet rt p wStart wY- -> IO ()-checkUnrelatedRepos allowUnrelatedRepos us them =- when ( not allowUnrelatedRepos && areUnrelatedRepos us them ) $- do confirmed <- promptYorn "Repositories seem to be unrelated. Proceed?"- unless confirmed $ do putStrLn "Cancelled."- exitSuccess---- | 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, 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- (toPend :> _) <-- readPendingAndWorking (UseIndex, ScanKnown, MyersDiff) repo Nothing- invalidateIndex repo- case unFreeLeft newP of- (Sealed p) -> makeNewPending repo uw $ toPend +>+ p---- | Add a FL of patches starting from the working state to the pending patch,--- including as much extra context as is necessary (context meaning--- 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, 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- invalidateIndex repo- case genCommuteWhatWeCanRL commuteFL (reverseFL toUnrec :> p) of- (toP' :> p' :> _excessUnrec) ->- makeNewPending repo uw $ toPend +>+ reverseRL toP' +>+ p'---- | Get a list of all files and directories in the working copy, including--- boring files if necessary-listFiles :: Bool -> IO [String]-listFiles takeBoring =- do- nonboring <- considered emptyTree- working <- expand =<< applyTreeFilter nonboring <$> readPlainTree "."- return $ map (anchorPath "" . fst) $ list working- where- considered = if takeBoring- then const (return restrictDarcsdir)- else restrictBoring---- | 'listUnregisteredFiles' returns the list of all non-boring unregistered--- files in the repository.-listUnregisteredFiles :: Bool -> IO [String]-listUnregisteredFiles includeBoring =- do unregd <- listFiles includeBoring- regd <- listRegisteredFiles- return $ unregd \\ regd -- (inefficient)---- | 'listRegisteredFiles' returns the list of all registered files in the repository.-listRegisteredFiles :: IO [String]-listRegisteredFiles =- do recorded <- expand =<< withRepository YesUseCache (RepoJob readRecordedAndPending)- return $ map (anchorPath "" . fst) $ list recorded
@@ -47,7 +47,7 @@ import Darcs.Util.Exception ( prettyException ) import Darcs.Util.Progress ( beginTedious, endTedious, tediousSize, finishedOneIO )-import Darcs.Util.Printer ( hPutDocLn, RenderMode(..) )+import Darcs.Util.Printer ( hPutDocLn ) import Darcs.Util.Printer.Color ( showDoc ) import Darcs.Util.External ( backupByCopying, backupByRenaming ) import Darcs.Util.Path ( FileName, fn2fp )@@ -66,9 +66,9 @@ mapM_ go items endTedious what where go item =- do finishedOneIO what (showDoc Encode $ paMessage item)+ do finishedOneIO what (showDoc $ paMessage item) runDefaultIO (paAction item) `catch` \e ->- do hPutDocLn Encode stderr $ paOnError item+ do hPutDocLn stderr $ paOnError item ioError e instance ApplyMonad Tree DefaultIO where
@@ -1,9 +1,6 @@-{-# LANGUAGE CPP #-}- module Darcs.Repository.Cache ( cacheHash , okayHash- , takeHash , Cache(..) , CacheType(..) , CacheLoc(..)@@ -29,11 +26,10 @@ , reportBadSources ) where -import Control.Monad ( liftM, when, guard, unless, filterM, forM_, mplus )-import qualified Data.ByteString as B (length, drop, ByteString )-import qualified Data.ByteString.Char8 as BC (unpack)+import Control.Monad ( liftM, when, unless, filterM, forM_, mplus )+import qualified Data.ByteString as B (length, ByteString ) import Data.List ( nub, intercalate )-import Data.Maybe ( catMaybes, listToMaybe, fromMaybe )+import Data.Maybe ( catMaybes, fromMaybe ) import System.FilePath.Posix ( (</>), joinPath, dropFileName ) import System.Directory ( createDirectoryIfMissing, removeFile, doesFileExist, doesDirectoryExist, getDirectoryContents,@@ -42,7 +38,7 @@ import System.IO ( hPutStrLn, stderr ) import System.Posix.Files ( createLink, linkCount, getSymbolicLinkStatus ) -import Darcs.Util.ByteString ( gzWriteFilePS, linesPS )+import Darcs.Util.ByteString ( gzWriteFilePS ) import Darcs.Util.Global ( darcsdir, addBadSource, isBadSource, addReachableSource, isReachableSource, getBadSourcesList, defaultRemoteDarcsCmd ) import Darcs.Util.External ( gzFetchFilePS, fetchFilePS@@ -54,8 +50,7 @@ import Darcs.Util.SignalHandler ( catchNonSignal ) import Darcs.Util.URL ( isValidLocalPath, isHttpUrl, isSshUrl ) import Darcs.Util.File ( withCurrentDirectory )-import Darcs.Util.Crypt.SHA1 ( sha1PS )-import Darcs.Util.Crypt.SHA256 ( sha256sum )+import Darcs.Util.Hash ( sha256sum ) import Darcs.Util.English ( englishNum, Noun(..), Pronoun(..) ) import Darcs.Util.Exception ( catchall ) import Darcs.Util.Progress ( progressList, debugMessage, debugFail )@@ -178,18 +173,10 @@ shaOfPs = sha256sum ps okayHash :: String -> Bool-okayHash s = length s `elem` [40, 64, 75]--takeHash :: B.ByteString -> Maybe (String, B.ByteString)-takeHash ps = do- h <- listToMaybe $ linesPS ps- let v = BC.unpack h- guard $ okayHash v- return (v, B.drop (B.length h) ps)+okayHash s = length s `elem` [64, 75] checkHash :: String -> B.ByteString -> Bool checkHash h s- | length h == 40 = (show $ sha1PS s) == h | length h == 64 = sha256sum s == h | length h == 75 = B.length s == read (take 10 h) && sha256sum s == drop 11 h
@@ -1,53 +1,53 @@-{-# LANGUAGE CPP, ScopedTypeVariables #-} module Darcs.Repository.Clone- ( createRepository- , cloneRepository+ ( 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 Control.Monad ( when )+import qualified Data.ByteString.Char8 as BC import Data.List( intercalate )-import Data.Maybe( catMaybes, isJust )+import Data.Maybe( catMaybes ) import System.FilePath( (</>) ) import System.Directory- ( createDirectory- , removeFile+ ( 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(..)+import Darcs.Repository.Create+ ( EmptyRepository(..)+ , createRepository+ , writePristine+ )+import Darcs.Repository.State ( invalidateIndex )+import Darcs.Repository.Pending ( tentativelyAddToPending )+import Darcs.Repository.Identify+ ( IdentifyRepo(..) , identifyRepositoryFor- , identifyRepository- , maybeIdentifyRepository- , readRepo+ , maybeIdentifyRepository )+import Darcs.Repository.Hashed+ ( readRepo , tentativelyRemovePatches- , tentativelyAddToPending , finalizeRepositoryChanges , createPristineDirectoryTree- , setScriptsExecutable- , setScriptsExecutablePatches- , seekRepo- , repoPatchType , revertRepositoryChanges )+import Darcs.Repository.Working+ ( setScriptsExecutable+ , setScriptsExecutablePatches ) import Darcs.Repository.InternalTypes- ( modifyCache )-import Darcs.Repository.Job ( RepoJob(..), withRepoLock, withRepository )+ ( Repository+ , repoLocation+ , repoFormat+ , repoCache+ , modifyCache+ , repoPatchType )+import Darcs.Repository.Job ( withUMaskFlag ) import Darcs.Repository.Cache ( unionRemoteCaches , unionCaches@@ -60,22 +60,19 @@ ) 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 qualified Darcs.Repository.Hashed as HashedRepo+import Darcs.Repository.ApplyPatches ( runDefault )+import Darcs.Repository.Hashed+ ( applyToTentativePristineCwd+ , peekPristineHash ) import Darcs.Repository.Format ( RepoProperty ( HashedInventory, Darcs2 ) , RepoFormat- , createRepoFormat , formatHas- , writeRepoFormat , readProblem )-import Darcs.Repository.Prefs ( writeDefaultPrefs, addRepoSource, deleteSources )+import Darcs.Repository.Prefs ( addRepoSource, deleteSources ) import Darcs.Repository.Match ( getOnePatchset ) import Darcs.Util.External ( copyFileOrUrl@@ -83,8 +80,7 @@ , gzFetchFilePS ) import Darcs.Repository.PatchIndex- ( createOrUpdatePatchIndexDisk- , doesPatchIndexExist+ ( doesPatchIndexExist , createPIWithInterrupt ) import Darcs.Repository.Packs@@ -92,11 +88,7 @@ , fetchAndUnpackPatches , packsDir )-import Darcs.Util.Lock- ( writeBinFile- , writeDocBinFile- , appendBinFile- )+import Darcs.Util.Lock ( appendTextFile, withNewDirectory ) import Darcs.Repository.Flags ( UpdateWorking(..) , UseCache(..)@@ -116,12 +108,12 @@ , PatchFormat (..) ) -import Darcs.Patch ( RepoPatch, IsRepoType, apply, invert, effect, PrimOf )+import Darcs.Patch ( RepoPatch, IsRepoType, apply, invert, effect ) import Darcs.Patch.Depends ( findCommonWithThem, countUsThem ) import Darcs.Patch.Set ( Origin- , PatchSet(..)- , newset2RL- , newset2FL+ , PatchSet+ , patchSet2RL+ , patchSet2FL , progressPatchSet ) import Darcs.Patch.Match ( MatchFlag(..), havePatchsetMatch )@@ -140,19 +132,15 @@ ) 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.Printer ( Doc, text, hPutDocLn, putDocLn, errorDoc ) import Darcs.Util.Progress ( debugMessage , tediousSize@@ -160,31 +148,6 @@ , 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- joinUrl :: [String] -> String joinUrl = intercalate "/" @@ -203,41 +166,45 @@ -> 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+cloneRepository repodir mysimplename v useCache cloneKind um rdarcs sse remoteRepos+ setDefault matchFlags rfsource withWorkingDir usePatchIndex usePacks+ forget =+ withUMaskFlag um $ withNewDirectory mysimplename $ do+ let patchfmt = if formatHas Darcs2 rfsource then PatchFormat2 else PatchFormat1+ EmptyRepository toRepo' <-+ createRepository patchfmt withWorkingDir+ (if cloneKind == LazyClone then NoPatchIndex else usePatchIndex) useCache+ 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+ fromRepo <- identifyRepositoryFor toRepo' useCache repodir+ let fromLoc = repoLocation fromRepo+ let rffrom = repoFormat fromRepo case readProblem rffrom of- Just e -> fail $ "Incompatibility with repository " ++ fromDir ++ ":\n" ++ e+ Just e -> fail $ "Incompatibility with repository " ++ fromLoc ++ ":\n" ++ e Nothing -> return ()- debugMessage "Copying prefs"- copyFileOrUrl (remoteDarcs rdarcs) (joinUrl [fromDir, darcsdir, "prefs", "prefs"])+ debugMessage "Copying prefs..."+ copyFileOrUrl (remoteDarcs rdarcs)+ (joinUrl [fromLoc, 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+ debugMessage "Copying sources..."+ cache <- unionRemoteCaches (repoCache toRepo') (repoCache fromRepo) fromLoc+ appendTextFile (darcsdir </> "prefs/sources")+ (show $ repo2cache fromLoc `unionCaches` dropNonRepos cache)+ debugMessage "Done copying and filtering sources."+ -- put remote source last+ let toRepo = modifyCache toRepo' (const $ cache `unionCaches` repo2cache fromLoc) if formatHas HashedInventory rffrom then do -- copying basic repository (hashed_inventory and pristine)- if usePacks && (not . isValidLocalPath) fromDir+ if usePacks && (not . isValidLocalPath) fromLoc 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+ if usePacks && (not . isValidLocalPath) fromLoc then copyCompleteRepoPacked fromRepo toRepo v cloneKind else copyCompleteRepoNotPacked fromRepo toRepo v cloneKind else@@ -245,8 +212,8 @@ -- 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..."+ when (havePatchsetMatch (repoPatchType toRepo) matchFlags) $ do+ putInfo v $ text "Going to specified version..." -- the following is necessary to be able to read repo's patches revertRepositoryChanges toRepo YesUpdateWorking patches <- readRepo toRepo@@ -266,24 +233,33 @@ when (sse == YesSetScriptsExecutable) $ setScriptsExecutablePatches (invert $ effect ps) when (forget == YesForgetParent) deleteSources +-- | This keeps only NonWritable Repo entries.+dropNonRepos :: Cache -> Cache+dropNonRepos (Ca cache) = Ca $ filter notRepo cache where+ 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+ putInfo :: Verbosity -> Doc -> IO () putInfo Quiet _ = return ()-putInfo _ d = hPutDocLn Encode stderr d+putInfo _ d = hPutDocLn 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+copyBasicRepoNotPacked :: forall rt p wR wU wT.+ 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+copyBasicRepoNotPacked fromRepo toRepo verb rdarcs withWorkingDir = do putVerbose verb $ text "Copying hashed inventory from remote repo..."- HashedRepo.copyHashedInventory toRepo rdarcs fromDir+ HashedRepo.copyHashedInventory toRepo rdarcs (repoLocation fromRepo) putVerbose verb $ text "Writing pristine and working directory contents..." createPristineDirectoryTree toRepo "." withWorkingDir @@ -293,31 +269,33 @@ -> Verbosity -> CloneKind -> IO ()-copyCompleteRepoNotPacked _ torepository@(Repo todir _ _ _) verb cloneKind = do+copyCompleteRepoNotPacked _ toRepo verb cloneKind = do let cleanup = putInfo verb $ text "Using lazy repository." allowCtrlC cloneKind cleanup $ do- fetchPatchesIfNecessary torepository- pi <- doesPatchIndexExist todir- when pi $ createPIWithInterrupt torepository+ fetchPatchesIfNecessary toRepo+ pi <- doesPatchIndexExist (repoLocation toRepo)+ ps <- readRepo toRepo+ when pi $ createPIWithInterrupt toRepo ps 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+ forall rt p wR wU wT.+ 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 = joinUrl [fromDir, darcsdir, packsDir, "pristine"]+copyBasicRepoPacked fromRepo toRepo verb rdarcs withWorkingDir =+ do let fromLoc = repoLocation fromRepo+ let hashURL = joinUrl [fromLoc, darcsdir, packsDir, "pristine"] mPackHash <- (Just <$> gzFetchFilePS hashURL Uncachable) `catchall` (return Nothing)- let hiURL = joinUrl [fromDir, darcsdir, "hashed_inventory"]+ let hiURL = joinUrl [fromLoc, darcsdir, "hashed_inventory"] i <- gzFetchFilePS hiURL Uncachable- let currentHash = BS.pack $ inv2pris i- let copyNormally = copyBasicRepoNotPacked r toRepo verb rdarcs withWorkingDir+ let currentHash = BC.pack $ peekPristineHash i+ let copyNormally = copyBasicRepoNotPacked fromRepo toRepo verb rdarcs withWorkingDir case mPackHash of Just packHash | packHash == currentHash- -> ( copyBasicRepoPacked2 r toRepo verb withWorkingDir+ -> ( copyBasicRepoPacked2 fromRepo toRepo verb withWorkingDir `catch` \(e :: SomeException) -> do putStrLn ("Exception while getting basic pack:\n" ++ show e) copyNormally)@@ -325,53 +303,53 @@ 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+ forall rt p wR wU wT.+ 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+copyBasicRepoPacked2 fromRepo toRepo 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+ fetchAndUnpackBasic (repoCache toRepo) (repoLocation fromRepo) 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)+ 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 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 )+copyCompleteRepoPacked from to verb cloneKind =+ copyCompleteRepoPacked2 from 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 from to verb cloneKind copyCompleteRepoPacked2 ::- forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)+ forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, 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+copyCompleteRepoPacked2 fromRepo toRepo 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+ fetchAndUnpackPatches (mapRL hashedPatchFileName $ patchSet2RL us)+ (repoCache toRepo) (repoLocation fromRepo)+ pi <- doesPatchIndexExist (repoLocation toRepo)+ when pi $ createPIWithInterrupt toRepo us -- TODO or do another readRepo? cleanDir :: FilePath -> IO () cleanDir d = mapM_ (\x -> removeFile $ d </> x) .@@ -383,35 +361,35 @@ -> Verbosity -> WithWorkingDir -> IO ()-copyRepoOldFashioned fromrepository toRepo@(Repo _ _ _ toCache) verb withWorkingDir = do+copyRepoOldFashioned fromrepository toRepo verb withWorkingDir = do HashedRepo.revertTentativeChanges patches <- readRepo fromrepository let k = "Copying patch" beginTedious k- tediousSize k (lengthRL $ newset2RL patches)+ tediousSize k (lengthRL $ patchSet2RL patches) let patches' = progressPatchSet k patches- HashedRepo.writeTentativeInventory toCache GzipCompression patches'+ HashedRepo.writeTentativeInventory (repoCache toRepo) 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+ let patchesToApply = progressFL "Applying patch" $ patchSet2FL local_patches+ sequence_ $ mapFL applyToTentativePristineCwd $ 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)+fetchPatchesIfNecessary :: forall rt p wR wU wT. (IsRepoType rt, RepoPatch p) => Repository rt p wR wU wT -> IO ()-fetchPatchesIfNecessary torepository@(Repo _ _ _ c) =- do r <- readRepo torepository+fetchPatchesIfNecessary toRepo =+ do ps <- readRepo toRepo pipelineLength <- maxPipelineLength- let patches = newset2RL r+ let patches = patchSet2RL ps ppatches = progressRLShowTags "Copying patches" patches (first, other) = splitAt (pipelineLength - 1) $ tail $ hashes patches speculate | pipelineLength > 1 = [] : first : map (:[]) other@@ -423,56 +401,54 @@ fetchAndSpeculate (f, ss) = do _ <- fetchFileUsingCache c HashedPatchesDir f mapM_ (speculateFileUsingCache c HashedPatchesDir) ss+ c = repoCache toRepo +{- -- | 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.+-- bf: no it is not used anywhere 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+patchSetToRepository fromRepo patchset useCache rDarcs = do+ when (formatHas HashedInventory (repoFormat fromRepo)) $+ -- 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)+ let fromLoc = repoLocation fromRepo+ HashedRepo.copyHashedInventory repox rDarcs fromLoc+ void $ copySources repox fromLoc+ repo <- writePatchSet patchset useCache+ readRepo repo >>= (runDefault . applyPatches . patchSet2FL) debugMessage "Writing the pristine"- withCurrentDirectory dir $ readWorking >>= replacePristine repo+ withRepoLocation repo $ 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)+writePatchSet :: (IsRepoType rt, RepoPatch p) => PatchSet rt p Origin wX -> UseCache -> IO (Repository rt p wR wU wT) writePatchSet patchset useCache = do maybeRepo <- maybeIdentifyRepository useCache "."- let repo@(Repo _ _ _ c) =+ let repo = 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.writeTentativeInventory (repoCache repo) 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+replacePristine = writePristine . repoLocation allowCtrlC :: CloneKind -> IO () -> IO () -> IO () allowCtrlC CompleteClone _ action = action@@ -482,30 +458,3 @@ 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
@@ -0,0 +1,165 @@+module Darcs.Repository.Create+ ( createRepository+ , createRepositoryV1+ , createRepositoryV2+ , EmptyRepository(..)+ , writePristine+ ) where++import Prelude ()+import Darcs.Prelude++import Control.Monad ( when )+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC+import Data.Maybe( isJust )+import System.Directory+ ( createDirectory+ , getCurrentDirectory+ , setCurrentDirectory+ )+import System.FilePath ( (</>) )+import System.IO.Error+ ( catchIOError+ , isAlreadyExistsError+ )++import Darcs.Patch ( RepoPatch )+import Darcs.Patch.Apply( ApplyState )+import Darcs.Patch.RepoType ( RepoType(..), RebaseType(..) )+import Darcs.Patch.Set ( Origin, emptyPatchSet )+import Darcs.Patch.V1 ( RepoPatchV1 )+import Darcs.Patch.V2 ( RepoPatchV2 )+import qualified Darcs.Patch.V1.Prim as V1 ( Prim(..) )+import qualified Darcs.Patch.V2.Prim as V2 ( Prim(..) )++import Darcs.Repository.Cache ( Cache )+import Darcs.Repository.Format+ ( RepoFormat+ , createRepoFormat+ , writeRepoFormat+ )+import Darcs.Repository.Flags+ ( UseCache(..)+ , WithWorkingDir (..)+ , WithPatchIndex (..)+ , PatchFormat (..)+ )+import Darcs.Repository.Hashed+ ( pokePristineHash+ , pristineDirPath+ , patchesDirPath+ , inventoriesDirPath+ , hashedInventoryPath+ )+import Darcs.Repository.Identify ( seekRepo )+import Darcs.Repository.InternalTypes+ ( Repository+ , PristineType(..)+ , mkRepo+ )+import Darcs.Repository.PatchIndex ( createOrUpdatePatchIndexDisk )+import Darcs.Repository.Prefs+ ( writeDefaultPrefs+ , getCaches+ , prefsDirPath+ )++import Darcs.Util.ByteString( gzReadFilePS )+import Darcs.Util.File ( withCurrentDirectory )+import Darcs.Util.Global ( darcsdir )+import Darcs.Util.Hash( encodeBase16 )+import Darcs.Util.Lock+ ( writeBinFile+ , writeDocBinFile+ )+import Darcs.Util.Tree( Tree, emptyTree )+import Darcs.Util.Tree.Hashed( writeDarcsHashed, darcsAddMissingHashes )++createRepositoryFiles :: PatchFormat -> WithWorkingDir -> IO RepoFormat+createRepositoryFiles patchfmt withWorkingDir = do+ cwd <- getCurrentDirectory+ x <- seekRepo+ when (isJust x) $ do+ setCurrentDirectory cwd+ putStrLn "WARNING: creating a nested repository."+ createDirectory darcsdir `catchIOError`+ (\e-> if isAlreadyExistsError e+ then fail "Tree has already been initialized!"+ else fail $ "Error creating directory `"++darcsdir++"'.")+ createDirectory pristineDirPath+ createDirectory patchesDirPath+ createDirectory inventoriesDirPath+ createDirectory prefsDirPath+ writeDefaultPrefs+ let repo_format = createRepoFormat patchfmt withWorkingDir+ writeRepoFormat repo_format (darcsdir </> "format")+ -- note: all repos we create nowadays are hashed+ writeBinFile hashedInventoryPath B.empty+ writePristine here emptyTree+ return repo_format++data EmptyRepository where+ EmptyRepository :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository ('RepoType 'NoRebase) p Origin Origin Origin+ -> EmptyRepository++createRepository :: PatchFormat -> WithWorkingDir -> WithPatchIndex -> UseCache+ -> IO EmptyRepository+createRepository patchfmt withWorkingDir withPatchIndex useCache = do+ rfmt <- createRepositoryFiles patchfmt withWorkingDir+ cache <- getCaches useCache here+ repo@(EmptyRepository r) <- case patchfmt of+ PatchFormat1 -> return $ EmptyRepository $ mkRepoV1 rfmt cache+ PatchFormat2 -> return $ EmptyRepository $ mkRepoV2 rfmt cache+ maybeCreatePatchIndex withPatchIndex r+ return repo++mkRepoV1+ :: RepoFormat+ -> Cache+ -> Repository ('RepoType 'NoRebase) (RepoPatchV1 V1.Prim) Origin Origin Origin+mkRepoV1 repofmt cache = mkRepo "." repofmt HashedPristine cache++mkRepoV2+ :: RepoFormat+ -> Cache+ -> Repository ('RepoType 'NoRebase) (RepoPatchV2 V2.Prim) Origin Origin Origin+mkRepoV2 repofmt cache = mkRepo "." repofmt HashedPristine cache++createRepositoryV1+ :: WithWorkingDir -> WithPatchIndex -> UseCache+ -> IO (Repository ('RepoType 'NoRebase) (RepoPatchV1 V1.Prim) Origin Origin Origin)+createRepositoryV1 withWorkingDir withPatchIndex useCache = do+ rfmt <- createRepositoryFiles PatchFormat1 withWorkingDir+ cache <- getCaches useCache here+ let repo = mkRepoV1 rfmt cache+ maybeCreatePatchIndex withPatchIndex repo+ return repo++createRepositoryV2+ :: WithWorkingDir -> WithPatchIndex -> UseCache+ -> IO (Repository ('RepoType 'NoRebase) (RepoPatchV2 V2.Prim) Origin Origin Origin)+createRepositoryV2 withWorkingDir withPatchIndex useCache = do+ rfmt <- createRepositoryFiles PatchFormat2 withWorkingDir+ cache <- getCaches useCache here+ let repo = mkRepoV2 rfmt cache+ maybeCreatePatchIndex withPatchIndex repo+ return repo++maybeCreatePatchIndex :: (RepoPatch p, ApplyState p ~ Tree)+ => WithPatchIndex -> Repository rt p Origin wU Origin -> IO ()+maybeCreatePatchIndex NoPatchIndex _ = return ()+maybeCreatePatchIndex YesPatchIndex repo =+ createOrUpdatePatchIndexDisk repo emptyPatchSet++writePristine :: FilePath -> Tree IO -> IO ()+writePristine dir tree =+ withCurrentDirectory dir $ do+ inv <- gzReadFilePS hashedInventoryPath+ tree' <- darcsAddMissingHashes tree+ root <- writeDarcsHashed tree' pristineDirPath+ writeDocBinFile hashedInventoryPath $ pokePristineHash (BC.unpack $ encodeBase16 root) inv++here :: String+here = "."
@@ -28,7 +28,6 @@ -- Stability : experimental -- Portability : portable -{-# LANGUAGE CPP #-} module Darcs.Repository.Diff ( treeDiff@@ -38,7 +37,7 @@ import Darcs.Prelude import qualified Data.ByteString.Lazy.Char8 as BLC-import qualified Data.ByteString as BS+import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import Data.List ( sortBy )@@ -69,9 +68,6 @@ import Darcs.Patch.Witnesses.Sealed ( Gap(..) ) import Darcs.Repository.Flags ( DiffAlgorithm(..) ) -#include "impossible.h"-- data Diff m = Added (TreeItem m) | Removed (TreeItem m) | Changed (TreeItem m) (TreeItem m)@@ -87,7 +83,7 @@ getDiff _ Nothing Nothing = impossible -- zipTrees should never return this -treeDiff :: forall m w prim . (Functor m, Monad m, Gap w, PrimPatch prim)+treeDiff :: forall m w prim . (Monad m, Gap w, PrimPatch prim) => DiffAlgorithm -> (FilePath -> FileType) -> Tree m@@ -148,12 +144,40 @@ | BL.null a && BL.null b = emptyGap NilFL | BL.null a = freeGap (diff_from_empty p b) | BL.null b = freeGap (diff_to_empty p a)++ -- What is 'a line'? One view is that a line is something that is+ -- /terminated/ by either a newline or end of file. Another view is+ -- that lines are /separated/ by newline symbols.+ --+ -- The first view is the more "intuitive" one. The second is more+ -- "technical", it has the simpler definition and the highly desirable+ -- property that splitting a text into lines and joining them with+ -- newline symbols are inverse operations. The last point is the reason+ -- we never use the standard versions of 'unlines' for ByteString+ -- anywhere in darcs.+ --+ -- The two views differ mostly when enumerating the lines of a file+ -- that ends with a newline symbol: here, the technical view counts one+ -- more (empty) line. This leads to un-intuitive (though technically+ -- not incorrect) results when calculating the diff for a change that+ -- appends an empty line to a file that already has a newline at the+ -- end. For instance, for a file with a single, newline-terminated line+ -- of text, the LCS algorithm would tell us that a *third* (empty) line+ -- is being added.+ --+ -- To avoid this, we add a special case here: we strip off common+ -- newline symbols at the end. When we later split the result into+ -- lines for the diff algorithm, it never gets to see the empty+ -- last lines in both files and thus gives us the more intuitive result.++ | BLC.last a == '\n' && BLC.last b == '\n'+ = freeGap (line_diff p (linesB $ BLC.init a) (linesB $ BLC.init b)) | otherwise = freeGap (line_diff p (linesB a) (linesB b)) line_diff p a b = canonize da (hunk p 1 a b) diff_to_empty p x | BLC.last x == '\n' = line_diff p (init $ linesB x) []- | otherwise = line_diff p (linesB x) [BS.empty]+ | otherwise = line_diff p (linesB x) [B.empty] diff_from_empty p x = invert (diff_to_empty p x) @@ -161,4 +185,4 @@ linesB = map strict . BLC.split '\n' - strict = BS.concat . BL.toChunks+ strict = B.concat . BL.toChunks
@@ -30,6 +30,8 @@ , ForgetParent (..) , PatchFormat (..) , IncludeBoring (..)+ , HooksConfig (..)+ , HookConfig (..) ) where import Darcs.Util.Diff ( DiffAlgorithm(..) )@@ -129,3 +131,13 @@ data PatchFormat = PatchFormat1 | PatchFormat2 deriving ( Eq, Show )++data HooksConfig = HooksConfig+ { pre :: HookConfig+ , post :: HookConfig+ }++data HookConfig = HookConfig+ { cmd :: Maybe String+ , prompt :: Bool+ }
@@ -2,8 +2,6 @@ -- -- This file is licensed under the GPL, version two or later. -{-# LANGUAGE CPP #-}- module Darcs.Repository.Format ( RepoFormat(..) , RepoProperty(..)@@ -22,10 +20,8 @@ import Prelude () import Darcs.Prelude -#include "impossible.h"- import Control.Monad ( mplus, (<=<) )-import qualified Data.ByteString.Char8 as BC ( split, unpack, elemIndex )+import qualified Data.ByteString.Char8 as BC ( split, pack, unpack, elemIndex ) import qualified Data.ByteString as B ( null, empty ) import Data.List ( partition, intercalate, (\\) ) import Data.Maybe ( isJust, mapMaybe )@@ -149,7 +145,8 @@ -- | Write the repo format to the given file. writeRepoFormat :: RepoFormat -> FilePath -> IO ()-writeRepoFormat rf loc = writeBinFile loc $ show rf+writeRepoFormat rf loc = writeBinFile loc $ BC.pack $ show rf+-- note: this assumes show returns ascii -- | 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.
@@ -0,0 +1,1101 @@+-- Copyright (C) 2006-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; if not, write to the Free Software Foundation,+-- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.++module Darcs.Repository.Hashed+ ( inventoriesDir+ , inventoriesDirPath+ , pristineDir+ , pristineDirPath+ , patchesDir+ , patchesDirPath+ , hashedInventory+ , hashedInventoryPath++ , revertTentativeChanges+ , revertRepositoryChanges+ , finalizeTentativeChanges+ , cleanPristine+ , filterDirContents+ , cleanInventories+ , cleanPatches+ , copyPristine+ , copyPartialsPristine+ , applyToTentativePristine+ , applyToTentativePristineCwd+ , addToTentativeInventory+ , readRepo+ , readRepoHashed+ , readTentativeRepo+ , writeAndReadPatch+ , writeTentativeInventory+ , copyHashedInventory+ , readHashedPristineRoot+ , pokePristineHash+ , peekPristineHash+ , listInventories+ , listInventoriesLocal+ , listInventoriesRepoDir+ , listPatchesLocalBucketed+ , writePatchIfNecessary+ , diffHashLists+ , withRecorded+ , withTentative+ , tentativelyAddPatch+ , tentativelyRemovePatches+ , tentativelyRemovePatches_+ , tentativelyAddPatch_+ , tentativelyAddPatches_+ , tentativelyReplacePatches+ , finalizeRepositoryChanges+ , unrevertUrl+ , createPristineDirectoryTree+ , createPartialsPristineDirectoryTree+ , reorderInventory+ , cleanRepository+ , UpdatePristine(..)+ , repoXor+ ) where++import Prelude ()+import Darcs.Prelude++import Control.Arrow ( (&&&) )+import Control.Exception ( catch, IOException )+import Darcs.Util.Exception ( catchall )+import Control.Monad ( when, unless, void )+import Data.Maybe+import Data.List( foldl' )++import qualified Data.ByteString as B ( empty, readFile, append )+import qualified Data.ByteString.Char8 as BC ( unpack, pack )+import qualified Data.Set as Set++import Darcs.Util.Hash( encodeBase16, Hash(..), SHA1, sha1Xor, sha1zero )+import Darcs.Util.Prompt ( promptYorn )+import Darcs.Util.Tree( treeHash, Tree )+import Darcs.Util.Tree.Hashed( hashedTreeIO, readDarcsHashedNosize,+ readDarcsHashed, writeDarcsHashed,+ decodeDarcsHash, decodeDarcsSize )+import Darcs.Util.SignalHandler ( withSignalsBlocked )++import System.Directory ( createDirectoryIfMissing, getDirectoryContents+ , doesFileExist, doesDirectoryExist )+import System.FilePath.Posix( (</>) )+import System.IO.Unsafe ( unsafeInterleaveIO )+import System.IO ( stderr, hPutStrLn )++import Darcs.Util.External+ ( copyFileOrUrl+ , cloneFile+ , fetchFilePS+ , gzFetchFilePS+ , Cachable( Uncachable )+ )+import Darcs.Repository.Flags ( Compression, RemoteDarcs, remoteDarcs+ , Verbosity(..), UpdateWorking (..), WithWorkingDir (WithWorkingDir) )++import Darcs.Repository.Format ( RepoProperty( HashedInventory ), formatHas )+import Darcs.Repository.Pending+ ( readPending+ , pendingName+ , tentativelyRemoveFromPending+ , finalizePending+ , setTentativePending+ , prepend+ )+import Darcs.Repository.PatchIndex ( createOrUpdatePatchIndexDisk, doesPatchIndexExist )+import Darcs.Repository.State ( readRecorded, updateIndex )++import Darcs.Util.Global ( darcsdir )+import Darcs.Util.Lock+ ( writeBinFile+ , writeDocBinFile+ , writeAtomicFilePS+ , appendDocBinFile+ , removeFileMayNotExist+ )+import Darcs.Patch.Set ( PatchSet(..), Tagged(..)+ , SealedPatchSet, Origin+ , patchSet2RL+ )++import Darcs.Patch.Show ( ShowPatch, ShowPatchFor(..) )+import Darcs.Patch.PatchInfoAnd+ ( PatchInfoAnd, Hopefully, patchInfoAndPatch, info+ , extractHash, createHashed, hopefully )+import Darcs.Patch ( IsRepoType, RepoPatch, showPatch, apply+ , description+ , commuteRL+ , readPatch+ , effect+ , invert+ )++import Darcs.Patch.Apply ( Apply, ApplyState )++import Darcs.Patch.Bundle ( scanBundle+ , makeBundleN+ )+import Darcs.Patch.Named.Wrapped ( namedIsInternal )+import Darcs.Patch.Read ( ReadPatch )+import Darcs.Patch.Depends ( removeFromPatchSet, slightlyOptimizePatchset+ , mergeThem, splitOnTag )+import Darcs.Patch.Info+ ( PatchInfo, displayPatchInfo, isTag, makePatchname )+import Darcs.Util.Path ( FilePathLike, ioAbsoluteOrRemote, toPath+ , AbsolutePath, toFilePath )+import Darcs.Repository.Cache ( Cache(..), fetchFileUsingCache,+ speculateFilesUsingCache, writeFileUsingCache,+ HashedDir(..), hashedDir, peekInCache, bucketFolder )+import Darcs.Repository.HashedIO ( copyHashed, copyPartialsHashed,+ cleanHashdir )+import Darcs.Repository.Inventory+import Darcs.Repository.InternalTypes+ ( Repository+ , repoCache+ , repoFormat+ , repoLocation+ , withRepoLocation+ , coerceT )+import qualified Darcs.Repository.Old as Old ( readOldRepo, oldRepoFailMsg )+import Darcs.Util.File ( withCurrentDirectory )+import Darcs.Patch.Witnesses.Ordered+ ( (+<+), FL(..), RL(..), mapRL, foldFL_M+ , (:>)(..), lengthFL, filterOutFLFL+ , reverseFL, reverseRL )+import Darcs.Patch.Witnesses.Sealed ( Sealed(..), seal, unseal, mapSeal )+import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )++import Darcs.Util.ByteString ( gzReadFilePS )+import Darcs.Util.Printer.Color ( showDoc )+import Darcs.Util.Printer+ ( Doc, hcat, ($$), renderString, renderPS, text, putDocLn, (<+>) )+import Darcs.Util.Progress ( beginTedious, endTedious, debugMessage, finishedOneIO )+import Darcs.Patch.Progress (progressFL)+import Darcs.Util.Workaround ( renameFile )+import Darcs.Repository.Prefs ( globalCacheDir )+++makeDarcsdirPath :: String -> String+makeDarcsdirPath name = darcsdir </> name++-- TODO rename xyzPath to xyzLocal to make it clear that it is+-- relative to the local darcsdir++-- Location of the (one and only) head inventory.+hashedInventory, hashedInventoryPath :: String+hashedInventory = "hashed_inventory"+hashedInventoryPath = makeDarcsdirPath hashedInventory++-- Location of the (one and only) tentative head inventory.+tentativeHashedInventory, tentativeHashedInventoryPath :: String+tentativeHashedInventory = "tentative_hashed_inventory"+tentativeHashedInventoryPath = makeDarcsdirPath tentativeHashedInventory++-- Location of parent inventories.+inventoriesDir, inventoriesDirPath :: String+inventoriesDir = "inventories"+inventoriesDirPath = makeDarcsdirPath inventoriesDir++-- Location of pristine trees.+pristineDir, tentativePristinePath, pristineDirPath :: String+tentativePristinePath = makeDarcsdirPath "tentative_pristine"+pristineDir = "pristine.hashed"+pristineDirPath = makeDarcsdirPath pristineDir++-- Location of patches.+patchesDir, patchesDirPath :: String+patchesDir = "patches"+patchesDirPath = makeDarcsdirPath patchesDir++-- | The way patchfiles, inventories, and pristine trees are stored.+-- 'PlainLayout' means all files are in the same directory. 'BucketedLayout'+-- means we create a second level of subdirectories, such that all files whose+-- hash starts with the same two letters are in the same directory.+data DirLayout = PlainLayout | BucketedLayout++-- | 'applyToHashedPristine' takes a root hash, a patch @p@ and attempts to+-- apply the patch to the 'Tree' identified by @h@. If we encounter an old,+-- size-prefixed pristine, we first convert it to the non-size-prefixed format,+-- then apply the patch.+applyToHashedPristine :: (Apply p, ApplyState p ~ Tree) => String -> p wX wY+ -> IO String+applyToHashedPristine h p = applyOrConvertOldPristineAndApply+ where+ applyOrConvertOldPristineAndApply =+ tryApply hash `catch` \(_ :: IOException) -> handleOldPristineAndApply++ hash = decodeDarcsHash $ BC.pack h++ failOnMalformedRoot (SHA256 _) = return ()+ failOnMalformedRoot root = fail $ "Cannot handle hash: " ++ show root++ hash2root = BC.unpack . encodeBase16++ tryApply :: Hash -> IO String+ tryApply root = do+ failOnMalformedRoot root+ -- Read a non-size-prefixed pristine, failing if we encounter one.+ tree <- readDarcsHashedNosize pristineDirPath root+ (_, updatedTree) <- hashedTreeIO (apply p) tree pristineDirPath+ return . hash2root $ treeHash updatedTree++ warn = "WARNING: Doing a one-time conversion of pristine format.\n"+ ++ "This may take a while. The new format is backwards-compatible."++ handleOldPristineAndApply = do+ hPutStrLn stderr warn+ inv <- gzReadFilePS hashedInventoryPath+ let oldroot = BC.pack $ peekPristineHash inv+ oldrootSizeandHash = (decodeDarcsSize &&& decodeDarcsHash) oldroot+ -- Read the old size-prefixed pristine tree+ old <- readDarcsHashed pristineDirPath oldrootSizeandHash+ -- Write out the pristine tree as a non-size-prefixed pristine.+ root <- writeDarcsHashed old pristineDirPath+ let newroot = hash2root root+ -- Write out the new inventory.+ writeDocBinFile hashedInventoryPath $ pokePristineHash newroot inv+ cleanHashdir (Ca []) HashedPristineDir [newroot]+ hPutStrLn stderr "Pristine conversion done..."+ -- Retry applying the patch, which should now succeed.+ tryApply root++-- |revertTentativeChanges swaps the tentative and "real" hashed inventory+-- files, and then updates the tentative pristine with the "real" inventory+-- hash.+revertTentativeChanges :: IO ()+revertTentativeChanges = do+ cloneFile hashedInventoryPath tentativeHashedInventoryPath+ i <- gzReadFilePS hashedInventoryPath+ writeBinFile tentativePristinePath $ B.append pristineName (BC.pack (peekPristineHash i))++-- |finalizeTentativeChanges trys to atomically swap the tentative+-- inventory/pristine pointers with the "real" pointers; it first re-reads the+-- inventory to optimize it, presumably to take account of any new tags, and+-- 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 :: (IsRepoType rt, RepoPatch p)+ => Repository rt p wR wU wT -> Compression -> IO ()+finalizeTentativeChanges r compr = do+ debugMessage "Optimizing the inventory..."+ -- Read the tentative patches+ ps <- readTentativeRepo r "."+ writeTentativeInventory (repoCache r) compr ps+ i <- gzReadFilePS tentativeHashedInventoryPath+ p <- gzReadFilePS tentativePristinePath+ -- Write out the "optimised" tentative inventory.+ writeDocBinFile tentativeHashedInventoryPath $ pokePristineHash (peekPristineHash p) i+ -- Atomically swap.+ renameFile tentativeHashedInventoryPath hashedInventoryPath++-- |readHashedPristineRoot attempts to read the pristine hash from the current+-- inventory, returning Nothing if it cannot do so.+readHashedPristineRoot :: Repository rt p wR wU wT -> IO (Maybe String)+readHashedPristineRoot r = withRepoLocation r $ do+ i <- (Just <$> gzReadFilePS hashedInventoryPath)+ `catch` (\(_ :: IOException) -> return Nothing)+ return $ peekPristineHash <$> i++-- |cleanPristine removes any obsolete (unreferenced) entries in the pristine+-- cache.+cleanPristine :: Repository rt p wR wU wT -> IO ()+cleanPristine r = withRepoLocation r $ do+ debugMessage "Cleaning out the pristine cache..."+ i <- gzReadFilePS hashedInventoryPath+ cleanHashdir (repoCache r) HashedPristineDir [peekPristineHash i]++-- |filterDirContents returns the contents of the directory @d@+-- except files whose names begin with '.' (directories . and ..,+-- hidden files) and files whose names are filtered by the function @f@, if+-- @dir@ is empty, no paths are returned.+filterDirContents :: FilePath -> (FilePath -> Bool) -> IO [FilePath]+filterDirContents d f = do+ let realPath = makeDarcsdirPath d+ exists <- doesDirectoryExist realPath+ if exists+ then filter (\x -> head x /= '.' && f x) <$>+ getDirectoryContents realPath+ else return []++-- | Set difference between two lists of hashes.+diffHashLists :: [String] -> [String] -> [String]+diffHashLists xs ys = from_set $ (to_set xs) `Set.difference` (to_set ys)+ where+ to_set = Set.fromList . map BC.pack+ from_set = map BC.unpack . Set.toList++-- |cleanInventories removes any obsolete (unreferenced) files in the+-- inventories directory.+cleanInventories :: Repository rt p wR wU wT -> IO ()+cleanInventories _ = do+ debugMessage "Cleaning out inventories..."+ hs <- listInventoriesLocal+ fs <- filterDirContents inventoriesDir (const True)+ mapM_ (removeFileMayNotExist . (inventoriesDirPath </>))+ (diffHashLists fs hs)++-- FIXME this is ugly, these files should be directly under _darcs+-- since they are not hashed. And 'unrevert' isn't even a real patch but+-- a patch bundle.+-- |specialPatches list of special patch files that may exist in the directory+-- _darcs/patches/.+specialPatches :: [FilePath]+specialPatches = ["unrevert", "pending", "pending.tentative"]++-- |cleanPatches removes any obsolete (unreferenced) files in the+-- patches directory.+cleanPatches :: Repository rt p wR wU wT -> IO ()+cleanPatches _ = do+ debugMessage "Cleaning out patches..."+ hs <- listPatchesLocal PlainLayout darcsdir darcsdir+ fs <- filterDirContents patchesDir (`notElem` specialPatches)+ mapM_ (removeFileMayNotExist . (patchesDirPath </>))+ (diffHashLists fs hs)+++-- |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 rt p wX wY -> IO FilePath+addToSpecificInventory invPath c compr p = do+ let invFile = makeDarcsdirPath invPath+ hash <- snd <$> writePatchIfNecessary c compr p+ appendDocBinFile invFile $+ showInventoryEntry (info p, hash)+ return $ patchesDirPath </> getValidHash hash++-- | Warning: this allows to add any arbitrary patch! Used by convert import.+addToTentativeInventory :: RepoPatch p => Cache -> Compression+ -> PatchInfoAnd rt p wX wY -> IO FilePath+addToTentativeInventory = addToSpecificInventory tentativeHashedInventory++-- | 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)+ => Repository rt p wR wU wT -> Compression+ -> FL (PatchInfoAnd rt p) wX wT -> IO ()+removeFromTentativeInventory repo compr to_remove = do+ debugMessage $ "Start removeFromTentativeInventory"+ allpatches <- readTentativeRepo repo "."+ remaining <- case removeFromPatchSet to_remove allpatches of+ Nothing -> bug "Hashed.removeFromTentativeInventory: precondition violated"+ Just r -> return r+ writeTentativeInventory (repoCache 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.+writeHashFile :: Cache -> Compression -> HashedDir -> Doc -> IO String+writeHashFile c compr subdir d = do+ debugMessage $ "Writing hash file to " ++ hashedDir subdir+ writeFileUsingCache c compr subdir $ renderPS d++-- |readRepo returns the "current" repo patchset.+readRepoHashed :: (IsRepoType rt, RepoPatch p) => Repository rt p wR wU wT+ -> String -> IO (PatchSet rt p Origin wR)+readRepoHashed = readRepoUsingSpecificInventory hashedInventory++-- |readRepo returns the tentative repo patchset.+readTentativeRepo :: (IsRepoType rt, RepoPatch p)+ => 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 :: (IsRepoType rt, RepoPatch p)+ => 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 (repoCache repo) realdir invPath+ `catch` \e -> do+ hPutStrLn stderr ("Invalid repository: " ++ realdir)+ ioError e+ return $ unsafeCoerceP ps+ where+ readRepoPrivate :: (IsRepoType rt, RepoPatch p) => Cache -> FilePath+ -> FilePath -> IO (SealedPatchSet rt p Origin)+ readRepoPrivate cache d iname = do+ inventory <- readInventoryPrivate (d </> darcsdir </> iname)+ readRepoFromInventoryList cache inventory++-- | Read a 'PatchSet' from the repository (assumed to be located at the+-- current working directory) by following the chain of 'Inventory's, starting+-- with the given one. The 'Cache' parameter is used to locate patches and parent+-- inventories, since not all of them need be present inside the current repo.+readRepoFromInventoryList+ :: (IsRepoType rt, RepoPatch p)+ => Cache+ -> Inventory+ -> IO (SealedPatchSet rt p Origin)+readRepoFromInventoryList cache = parseInv+ where+ parseInv :: (IsRepoType rt, RepoPatch p)+ => Inventory+ -> IO (SealedPatchSet rt p Origin)+ parseInv (Inventory Nothing ris) =+ mapSeal (PatchSet NilRL) <$> read_patches (reverse ris)+ parseInv (Inventory (Just h) []) =+ -- TODO could be more tolerant and create a larger PatchSet+ bug $ "bad inventory " ++ getValidHash h ++ " (no tag) in parseInv!"+ parseInv (Inventory (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 ts ps++ read_patches :: (IsRepoType rt, RepoPatch p) => [InventoryEntry]+ -> IO (Sealed (RL (PatchInfoAnd rt p) wX))+ read_patches [] = return $ seal NilRL+ read_patches allis@((i1, h1) : is1) =+ lift2Sealed (\p rest -> rest :<: i1 `patchInfoAndPatch` p) (rp is1)+ (createValidHashed h1 (const $ speculateAndParse h1 allis i1))+ where+ rp :: (IsRepoType rt, RepoPatch p) => [InventoryEntry]+ -> IO (Sealed (RL (PatchInfoAnd rt p) wX))+ rp [] = return $ seal NilRL+ rp [(i, h), (il, hl)] =+ lift2Sealed (\p rest -> rest :<: i `patchInfoAndPatch` p)+ (rp [(il, hl)])+ (createValidHashed h+ (const $ speculateAndParse h (reverse allis) i))+ rp ((i, h) : is) =+ lift2Sealed (\p rest -> rest :<: i `patchInfoAndPatch` p)+ (rp is)+ (createValidHashed h (parse i))++ lift2Sealed :: (forall wY wZ . q wY wZ -> p wX wY -> r wX wZ)+ -> IO (Sealed (p wX))+ -> (forall wB . IO (Sealed (q wB)))+ -> IO (Sealed (r wX))+ lift2Sealed f iox ioy = do+ Sealed x <- unseal seal <$> unsafeInterleaveIO iox+ Sealed y <- unseal seal <$> unsafeInterleaveIO ioy+ return $ seal $ f y x++ speculateAndParse h is i = speculate h is >> parse i h++ speculate :: PatchHash -> [InventoryEntry] -> IO ()+ speculate h is = do+ already_got_one <- peekInCache cache HashedPatchesDir (getValidHash h)+ unless already_got_one $+ speculateFilesUsingCache cache HashedPatchesDir (map (getValidHash . snd) is)++ parse :: ReadPatch p => PatchInfo -> PatchHash -> IO (Sealed (p wX))+ parse i h = do+ debugMessage ("Reading patch file: "++ showDoc (displayPatchInfo i))+ (fn, ps) <- fetchFileUsingCache cache HashedPatchesDir (getValidHash h)+ case readPatch ps of+ Just p -> return p+ Nothing -> fail $ unlines [ "Couldn't parse file " ++ fn+ , "which is patch"+ , renderString $ displayPatchInfo i ]++ read_ts :: (IsRepoType rt, RepoPatch p) => InventoryEntry+ -> InventoryHash -> IO (Sealed (RL (Tagged rt p) Origin))+ read_ts tag0 h0 = do+ contents <- unsafeInterleaveIO $ readTaggedInventoryFromHash (getValidHash h0)+ let is = reverse $ case contents of+ (Inventory (Just _) (_ : ris0)) -> ris0+ (Inventory Nothing ris0) -> ris0+ (Inventory (Just _) []) -> bug "inventory without tag!"+ Sealed ts <- unseal seal <$>+ unsafeInterleaveIO+ (case contents of+ (Inventory (Just h') (t' : _)) -> read_ts t' h'+ (Inventory (Just _) []) -> bug "inventory without tag!"+ (Inventory Nothing _) -> return $ seal NilRL)+ Sealed ps <- unseal seal <$> unsafeInterleaveIO (read_patches is)+ Sealed tag00 <- read_tag tag0+ return $ seal $ ts :<: Tagged tag00 (Just (getValidHash h0)) ps++ read_tag :: (IsRepoType rt, RepoPatch p) => InventoryEntry+ -> IO (Sealed (PatchInfoAnd rt p wX))+ read_tag (i, h) =+ mapSeal (patchInfoAndPatch i) <$> createValidHashed h (parse i)++ readTaggedInventoryFromHash :: String+ -> IO Inventory+ readTaggedInventoryFromHash invHash = do+ (fileName, pristineAndInventory) <-+ fetchFileUsingCache cache HashedInventoriesDir invHash+ case parseInventory pristineAndInventory of+ Just r -> return r+ Nothing -> fail $ unwords ["parse error in file", fileName]++-- | Read an inventory from a file. Fails with an error message if+-- file is not there or cannot be parsed.+readInventoryPrivate :: FilePath+ -> IO Inventory+readInventoryPrivate path = do+ inv <- skipPristineHash <$> gzFetchFilePS path Uncachable+ case parseInventory inv of+ Just r -> return r+ Nothing -> fail $ unwords ["parse error in file", path]++-- |copyRepo copies the hashed inventory of @repo@ to the repository located at+-- @remote@.+copyHashedInventory :: Repository rt p wR wU wT -> RemoteDarcs -> String -> IO ()+copyHashedInventory outrepo rdarcs inloc | remote <- remoteDarcs rdarcs = do+ let outloc = repoLocation outrepo+ createDirectoryIfMissing False (outloc ++ "/" ++ inventoriesDirPath)+ copyFileOrUrl remote (inloc </> hashedInventoryPath)+ (outloc </> hashedInventoryPath)+ Uncachable -- no need to copy anything but hashed_inventory!+ debugMessage "Done copying hashed inventory."++-- |writeAndReadPatch makes a patch lazy, by writing it out to disk (thus+-- forcing it), and then re-reads the patch lazily.+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+ where+ parse i h = do+ debugMessage ("Rereading patch file: "++ showDoc (displayPatchInfo i))+ (fn, ps) <- fetchFileUsingCache c HashedPatchesDir (getValidHash h)+ case readPatch ps of+ Just x -> return x+ Nothing -> fail $ unlines [ "Couldn't parse patch file " ++ fn+ , "which is"+ , renderString $ displayPatchInfo i]++ readp h i = do Sealed x <- createValidHashed h (parse i)+ return . patchInfoAndPatch i $ unsafeCoerceP x++createValidHashed :: PatchHash+ -> (PatchHash -> IO (Sealed (a wX)))+ -> IO (Sealed (Darcs.Patch.PatchInfoAnd.Hopefully a wX))+createValidHashed h f = createHashed (getValidHash h) (f . mkValidHash)++-- | writeTentativeInventory writes @patchSet@ as the tentative inventory.+writeTentativeInventory :: RepoPatch p => Cache -> Compression+ -> PatchSet rt p Origin wX -> IO ()+writeTentativeInventory cache compr patchSet = do+ debugMessage "in writeTentativeInventory..."+ createDirectoryIfMissing False inventoriesDirPath+ beginTedious tediousName+ hsh <- writeInventoryPrivate $ slightlyOptimizePatchset patchSet+ endTedious tediousName+ debugMessage "still in writeTentativeInventory..."+ case hsh of+ Nothing -> writeBinFile (makeDarcsdirPath tentativeHashedInventory) B.empty+ Just h -> do+ content <- snd <$> fetchFileUsingCache cache HashedInventoriesDir h+ writeAtomicFilePS (makeDarcsdirPath tentativeHashedInventory) content+ where+ tediousName = "Writing inventory"+ writeInventoryPrivate :: RepoPatch p => PatchSet rt p Origin wX+ -> IO (Maybe String)+ writeInventoryPrivate (PatchSet NilRL NilRL) = return Nothing+ writeInventoryPrivate (PatchSet NilRL ps) = do+ inventory <- sequence $ mapRL (writePatchIfNecessary cache compr) ps+ let inventorylist = showInventoryPatches (reverse inventory)+ hash <- writeHashFile cache compr HashedInventoriesDir inventorylist+ return $ Just hash+ writeInventoryPrivate+ (PatchSet xs@(_ :<: Tagged t _ _) x) = do+ resthash <- write_ts xs+ finishedOneIO tediousName $ fromMaybe "" resthash+ inventory <- sequence $ mapRL (writePatchIfNecessary cache compr)+ (NilRL :<: t +<+ x)+ let inventorylist = hcat (map showInventoryEntry $ reverse inventory)+ inventorycontents =+ case resthash of+ Just h -> text ("Starting with inventory:\n" ++ h) $$+ inventorylist+ Nothing -> inventorylist+ hash <- writeHashFile cache compr HashedInventoriesDir inventorycontents+ return $ Just hash+ where+ -- | write_ts 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 rt p) Origin wX+ -> IO (Maybe String)+ 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 rt p wX wY -> IO InventoryEntry+writePatchIfNecessary c compr hp = infohp `seq`+ case extractHash hp of+ Right h -> return (infohp, mkValidHash h)+ Left p -> do+ h <- writeHashFile c compr HashedPatchesDir (showPatch ForStorage p)+ return (infohp, mkValidHash h)+ where+ infohp = info hp++-- |listInventoriesWith returns a list of the inventories hashes.+-- The first argument is to choose directory format.+-- The first argument can be readInventoryPrivate or readInventoryLocalPrivate.+-- The second argument specifies whether the files are expected+-- to be stored in plain or in bucketed format.+-- The third argument is the directory of the parent inventory files.+-- The fourth argument is the directory of the head inventory file.+listInventoriesWith+ :: (FilePath -> IO Inventory)+ -> DirLayout+ -> String -> String -> IO [String]+listInventoriesWith readInv dirformat baseDir startDir = do+ mbStartingWithInv <- getStartingWithHash startDir hashedInventory+ followStartingWiths mbStartingWithInv+ where+ getStartingWithHash dir file = inventoryParent <$> readInv (dir </> file)++ invDir = baseDir </> inventoriesDir+ nextDir dir = case dirformat of+ BucketedLayout -> invDir </> bucketFolder dir+ PlainLayout -> invDir++ followStartingWiths Nothing = return []+ followStartingWiths (Just hash) = do+ let startingWith = getValidHash hash+ mbNextInv <- getStartingWithHash (nextDir startingWith) startingWith+ (startingWith :) <$> followStartingWiths mbNextInv++-- |listInventories returns a list of the inventories hashes.+-- This function attempts to retrieve missing inventory files.+listInventories :: IO [String]+listInventories =+ listInventoriesWith readInventoryPrivate PlainLayout darcsdir darcsdir++-- | Read the given inventory file if it exist, otherwise return an empty+-- inventory. Used when we expect that some inventory files may be missing.+readInventoryLocalPrivate :: FilePath -> IO Inventory+readInventoryLocalPrivate path = do+ b <- doesFileExist path+ if b then readInventoryPrivate path+ else return emptyInventory++-- | Return inventories hashes by following the head inventory.+-- This function does not attempt to retrieve missing inventory files.+listInventoriesLocal :: IO [String]+listInventoriesLocal =+ listInventoriesWith readInventoryLocalPrivate PlainLayout darcsdir darcsdir++-- |listInventoriesRepoDir returns a list of the inventories hashes.+-- The argument @repoDir@ is the directory of the repository from which+-- we are going to read the head inventory file.+-- The rest of hashed files are read from the global cache.+listInventoriesRepoDir :: String -> IO [String]+listInventoriesRepoDir repoDir = do+ gCacheDir' <- globalCacheDir+ let gCacheInvDir = fromJust gCacheDir'+ listInventoriesWith+ readInventoryLocalPrivate BucketedLayout gCacheInvDir (repoDir </> darcsdir)++-- | Return a list of the patch filenames, extracted from inventory+-- files, by starting with the head inventory and then following the+-- chain of parent inventories.+--+-- This function does not attempt to download missing inventory files.+--+-- * The first argument specifies whether the files are expected+-- to be stored in plain or in bucketed format.+-- * The second argument is the directory of the parent inventory.+-- * The third argument is the directory of the head inventory.+listPatchesLocal :: DirLayout -> String -> String -> IO [String]+listPatchesLocal dirformat baseDir startDir = do+ inventory <- readInventoryPrivate (startDir </> hashedInventory)+ followStartingWiths (inventoryParent inventory) (inventoryPatchNames inventory)+ where+ invDir = baseDir </> inventoriesDir+ nextDir dir = case dirformat of+ BucketedLayout -> invDir </> bucketFolder dir+ PlainLayout -> invDir++ followStartingWiths Nothing patches = return patches+ followStartingWiths (Just hash) patches = do+ let startingWith = getValidHash hash+ inv <- readInventoryLocalPrivate+ (nextDir startingWith </> startingWith)+ (patches++) <$> followStartingWiths (inventoryParent inv) (inventoryPatchNames inv)++-- |listPatchesLocalBucketed is similar to listPatchesLocal, but+-- it read the inventory directory under @darcsDir@ in bucketed format.+listPatchesLocalBucketed :: String -> String -> IO [String]+listPatchesLocalBucketed = listPatchesLocal BucketedLayout++-- | copyPristine copies a pristine tree into the current pristine dir,+-- and possibly copies a clean working copy.+-- The target is read from the passed-in dir/inventory name combination.+copyPristine :: Cache -> String -> String -> WithWorkingDir -> IO ()+copyPristine cache dir iname wwd = do+ i <- fetchFilePS (dir ++ "/" ++ iname) Uncachable+ debugMessage $ "Copying hashed pristine tree: " ++ peekPristineHash i+ let tediousName = "Copying pristine"+ beginTedious tediousName+ copyHashed tediousName cache wwd $ peekPristineHash i+ endTedious tediousName++-- |copyPartialsPristine copies the pristine entries for a given list of+-- filepaths.+copyPartialsPristine :: FilePathLike fp => Cache -> String+ -> String -> [fp] -> IO ()+copyPartialsPristine c d iname fps = do+ i <- fetchFilePS (d ++ "/" ++ iname) Uncachable+ copyPartialsHashed c (peekPristineHash i) fps++unrevertUrl :: Repository rt p wR wU wT -> String+unrevertUrl r = repoLocation r ++ "/"++darcsdir++"/patches/unrevert"++tentativelyAddPatch :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> Compression+ -> Verbosity+ -> UpdateWorking+ -> PatchInfoAnd rt p wT wY+ -> IO (Repository rt p wR wU wY)+tentativelyAddPatch = tentativelyAddPatch_ UpdatePristine++data UpdatePristine = UpdatePristine + | DontUpdatePristine+ | DontUpdatePristineNorRevert deriving Eq++tentativelyAddPatches_ :: (RepoPatch p, ApplyState p ~ Tree)+ => UpdatePristine+ -> Repository rt p wR wU wT+ -> Compression+ -> Verbosity+ -> UpdateWorking+ -> FL (PatchInfoAnd rt p) wT wY+ -> IO (Repository rt p wR wU wY)+tentativelyAddPatches_ up r c v uw ps =+ foldFL_M (\r' p -> tentativelyAddPatch_ up r' c v uw p) r ps++-- TODO re-add a safety catch for --dry-run? Maybe using a global, like dryRun+-- :: Bool, with dryRun = unsafePerformIO $ readIORef ...+tentativelyAddPatch_ :: (RepoPatch p, ApplyState p ~ Tree)+ => UpdatePristine+ -> Repository rt p wR wU wT+ -> Compression+ -> Verbosity+ -> UpdateWorking+ -> PatchInfoAnd rt p wT wY+ -> IO (Repository rt p wR wU wY)++tentativelyAddPatch_ up r compr verb uw p =+ withRepoLocation r $ do+ void $ addToTentativeInventory (repoCache r) compr p+ when (up == UpdatePristine) $ do debugMessage "Applying to pristine cache..."+ applyToTentativePristine r verb p+ debugMessage "Updating pending..."+ tentativelyRemoveFromPending r uw p+ return (coerceT r)+++-- |applyToTentativePristine applies a patch @p@ to the tentative pristine+-- tree, and updates the tentative pristine hash+applyToTentativePristine :: (ApplyState q ~ Tree, Apply q, ShowPatch q)+ => Repository rt p wR wU wT+ -> Verbosity+ -> q wT wY+ -> IO ()+applyToTentativePristine r verb p =+ withRepoLocation r $+ do when (verb == Verbose) $ putDocLn $ text "Applying to pristine..." <+> description p+ applyToTentativePristineCwd p++applyToTentativePristineCwd :: (ApplyState p ~ Tree, Apply p) => p wX wY+ -> IO ()+applyToTentativePristineCwd p = do+ tentativePristine <- gzReadFilePS tentativePristinePath+ -- Extract the pristine hash from the tentativePristine file, using+ -- peekPristineHash (this is valid since we normally just extract the hash from the+ -- first line of an inventory file; we can pass in a one-line file that+ -- just contains said hash).+ let tentativePristineHash = peekPristineHash tentativePristine+ newPristineHash <- applyToHashedPristine tentativePristineHash p+ writeDocBinFile tentativePristinePath $+ pokePristineHash newPristineHash tentativePristine++tentativelyRemovePatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> Compression+ -> UpdateWorking+ -> FL (PatchInfoAnd rt p) wX wT+ -> IO (Repository rt p wR wU wX)+tentativelyRemovePatches = tentativelyRemovePatches_ UpdatePristine++tentativelyRemovePatches_ :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => UpdatePristine+ -> Repository rt p wR wU wT+ -> Compression+ -> UpdateWorking+ -> FL (PatchInfoAnd rt p) wX wT+ -> IO (Repository rt p wR wU wX)+tentativelyRemovePatches_ up r compr uw ps =+ withRepoLocation r $ do+ when (up == UpdatePristine) $ do debugMessage "Adding changes to pending..."+ prepend r uw $ effect ps+ unless (up == DontUpdatePristineNorRevert) $ removeFromUnrevertContext r ps+ debugMessage "Removing changes from tentative inventory..."+ if formatHas HashedInventory (repoFormat r)+ then do removeFromTentativeInventory r compr ps+ when (up == UpdatePristine) $+ applyToTentativePristineCwd $+ progressFL "Applying inverse to pristine" $ invert ps+ else fail Old.oldRepoFailMsg+ return (coerceT r)++-- FIXME this is a rather weird API. If called with a patch that isn't already+-- in the repo, it fails with an obscure error from 'commuteToEnd'. It also+-- ends up redoing the work that the caller has already done - if it has+-- already commuted these patches to the end, it must also know the commuted+-- versions of the other patches in the repo.+-- |Given a sequence of patches anchored at the end of the current repository,+-- actually pull them to the end of the repository by removing any patches+-- 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 rt p wR wU wT wX+ . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> Compression+ -> UpdateWorking+ -> Verbosity+ -> FL (PatchInfoAnd rt p) wX wT+ -> IO ()+tentativelyReplacePatches repository compr uw verb ps =+ do let ps' = filterOutFLFL (namedIsInternal . hopefully) ps+ repository' <- tentativelyRemovePatches_ DontUpdatePristineNorRevert repository compr uw ps'+ mapAdd repository' ps'+ where mapAdd :: Repository rt p wM wL wI+ -> FL (PatchInfoAnd rt p) wI wJ+ -> IO ()+ mapAdd _ NilFL = return ()+ mapAdd r (a:>:as) =+ do r' <- tentativelyAddPatch_ DontUpdatePristine r compr verb uw a+ mapAdd r' as++-- The type here should rather be+-- ... -> Repo rt p wR wU wT -> IO (Repo rt p wT wU wT)+-- In other words: we set the recorded state to the tentative state.+finalizeRepositoryChanges :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> UpdateWorking+ -> Compression+ -> IO ()+finalizeRepositoryChanges r updateWorking compr+ | formatHas HashedInventory (repoFormat r) =+ withRepoLocation r $ do+ debugMessage "Finalizing changes..."+ withSignalsBlocked $ do+ finalizeTentativeChanges r compr+ recordedState <- readRecorded r+ finalizePending r updateWorking recordedState+ debugMessage "Done finalizing changes..."+ ps <- readRepo r+ doesPatchIndexExist (repoLocation r) >>= (`when` createOrUpdatePatchIndexDisk r ps)+ updateIndex r+ | otherwise = fail Old.oldRepoFailMsg++-- TODO: rename this and document the transaction protocol (revert/finalize)+-- clearly.+-- |Slightly confusingly named: as well as throwing away any tentative+-- changes, revertRepositoryChanges also re-initialises the tentative state.+-- It's therefore used before makign any changes to the repo.+-- So the type should rather be+--+-- > ... -> Repo rt p wR wU wT -> IO (Repo rt p wR wU wR)+revertRepositoryChanges :: RepoPatch p+ => Repository rt p wR wU wT+ -> UpdateWorking+ -> IO ()+revertRepositoryChanges r uw+ | formatHas HashedInventory (repoFormat r) =+ withRepoLocation r $+ do removeFileMayNotExist (pendingName ++ ".tentative")+ Sealed x <- readPending r+ setTentativePending r uw x+ when (uw == NoUpdateWorking) $ removeFileMayNotExist pendingName+ revertTentativeChanges+ | otherwise = fail Old.oldRepoFailMsg++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 r ps = do+ Sealed bundle <- unrevert_patch_bundle `catchall` return (seal (PatchSet NilRL NilRL))+ remove_from_unrevert_context_ bundle+ where unrevert_impossible =+ do confirmed <- promptYorn "This operation will make unrevert impossible!\nProceed?"+ if confirmed then removeFileMayNotExist (unrevertUrl r)+ else fail "Cancelled."+ unrevert_patch_bundle :: IO (SealedPatchSet rt p Origin)+ unrevert_patch_bundle = do pf <- B.readFile (unrevertUrl r)+ case scanBundle pf of+ Right foo -> return foo+ Left err -> fail $ "Couldn't parse unrevert patch:\n" ++ err+ 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..."+ debugMessage $ "Removing "++ show (lengthFL ps) +++ " patches in removeFromUnrevertContext!"+ ref <- readTentativeRepo r (repoLocation r)+ let withSinglet :: Sealed (FL ppp wXxx)+ -> (forall wYyy . ppp wXxx wYyy -> IO ()) -> IO ()+ withSinglet (Sealed (x :>: NilFL)) j = j x+ withSinglet _ _ = return ()+ withSinglet (mergeThem ref bundle) $ \h_us ->+ case commuteRL (reverseFL ps :> h_us) of+ Nothing -> unrevert_impossible+ Just (us' :> _) ->+ case removeFromPatchSet ps ref of+ Nothing -> unrevert_impossible+ Just common ->+ do debugMessage "Have now found the new context..."+ bundle' <- makeBundleN Nothing common (hopefully us':>:NilFL)+ writeDocBinFile (unrevertUrl r) bundle'+ debugMessage "Done adjusting the context of the unrevert changes!"++cleanRepository :: Repository rt p wR wU wT -> IO ()+cleanRepository r = cleanPristine r >> cleanInventories r >> cleanPatches r++-- | grab the pristine hash of _darcs/hash_inventory, and retrieve whole pristine tree,+-- possibly writing a clean working copy in the process.+createPristineDirectoryTree :: Repository rt p wR wU wT -> FilePath -> WithWorkingDir -> IO ()+createPristineDirectoryTree r reldir wwd+ | formatHas HashedInventory (repoFormat r) =+ do createDirectoryIfMissing True reldir+ withCurrentDirectory reldir $+ copyPristine (repoCache r) (repoLocation r) hashedInventoryPath wwd+ | otherwise = fail Old.oldRepoFailMsg++-- fp below really should be FileName+-- | Used by the commands dist and diff+createPartialsPristineDirectoryTree :: (FilePathLike fp)+ => Repository rt p wR wU wT+ -> [fp]+ -> FilePath+ -> IO ()+createPartialsPristineDirectoryTree r prefs dir+ | formatHas HashedInventory (repoFormat r) =+ do createDirectoryIfMissing True dir+ withCurrentDirectory dir $+ copyPartialsPristine (repoCache r) (repoLocation r)+ hashedInventoryPath prefs+ | otherwise = fail Old.oldRepoFailMsg++withRecorded :: Repository rt p wR wU wT+ -> ((AbsolutePath -> IO a) -> IO a)+ -> (AbsolutePath -> IO a)+ -> IO a+withRecorded repository mk_dir f+ = mk_dir $ \d -> do createPristineDirectoryTree repository (toFilePath d) WithWorkingDir+ f d++withTentative :: forall rt p a wR wU wT.+ Repository rt p wR wU wT+ -> ((AbsolutePath -> IO a) -> IO a)+ -> (AbsolutePath -> IO a)+ -> IO a+withTentative r mk_dir f+ | formatHas HashedInventory (repoFormat r) =+ mk_dir $ \d -> do copyPristine+ (repoCache r)+ (repoLocation r)+ (darcsdir++"/tentative_pristine")+ WithWorkingDir+ f d+ | otherwise = fail Old.oldRepoFailMsg++-- | Writes out a fresh copy of the inventory that minimizes the+-- amount of inventory that need be downloaded when people pull from+-- the repository.+--+-- Specifically, it breaks up the inventory on the most recent tag.+-- This speeds up most commands when run remotely, both because a+-- smaller file needs to be transfered (only the most recent+-- inventory). It also gives a guarantee that all the patches prior+-- 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 :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wR+ -> Compression+ -> UpdateWorking+ -> Verbosity+ -> IO ()+reorderInventory repository compr uw verb = do+ debugMessage "Reordering the inventory."+ PatchSet _ ps <- misplacedPatches `fmap` readRepo repository+ tentativelyReplacePatches repository compr uw verb $ reverseRL ps+ finalizeTentativeChanges repository compr+ debugMessage "Done reordering the inventory."++-- | Returns the patches that make the most recent tag dirty.+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.+ case filter isTag $ mapRL info $ patchSet2RL ps of+ [] -> ps+ (lt:_) -> + -- Take the most recent tag, and split the repository in,+ -- the clean PatchSet "up to" the tag (ts), and a RL of+ -- patches after the tag (r).+ case splitOnTag lt ps of+ 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)+ => Repository rt p wR wU wT+ -> IO (PatchSet rt p Origin wR)+readRepo r+ | formatHas HashedInventory (repoFormat r) = readRepoHashed r (repoLocation r)+ | otherwise = do Sealed ps <- Old.readOldRepo (repoLocation 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)+ => Repository rt p wR wU wR -> IO SHA1+repoXor repo = do+ hashes <- mapRL (makePatchname . info) . patchSet2RL <$> readRepo repo+ return $ foldl' sha1Xor sha1zero hashes+++
@@ -15,14 +15,12 @@ -- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. {-# OPTIONS_GHC -fno-warn-missing-methods #-}-{-# LANGUAGE CPP, TypeSynonymInstances, FlexibleInstances, MultiParamTypeClasses #-}+{-# LANGUAGE MultiParamTypeClasses #-} -module Darcs.Repository.HashedIO ( HashedIO,- copyHashed, copyPartialsHashed,+module Darcs.Repository.HashedIO ( copyHashed, copyPartialsHashed, cleanHashdir, getHashedFiles,- RW(RW) -- only exported to make warning go away- , pathsAndContents+ pathsAndContents ) where import Prelude ()@@ -49,8 +47,8 @@ , normPath , fp2fn , fn2fp- , fn2niceps- , niceps2fn+ , fn2ps+ , ps2fn , breakOnDir , ownName , superName@@ -72,23 +70,15 @@ readHashFile :: Cache -> HashedDir -> String -> IO (String,B.ByteString) readHashFile c subdir hash = do debugMessage $ "Reading hash file "++hash++" from "++hashedDir subdir++"/"- fetchFileUsingCache c subdir hash--data HashDir r p = HashDir { permissions :: !r, cache :: !Cache,- rootHash :: !String }-type HashedIO p = StateT (HashDir RW p) IO+ r <- fetchFileUsingCache c subdir hash+ debugMessage $ "Result of reading hash file: " ++ show r+ return r -data RW = RW-{--class Readable r where- isRO :: r -> Bool- isRO = const False-instance Readable RW-instance Readable RO where- isRO RO = True--}+data HashDir = HashDir { cache :: !Cache,+ rootHash :: !String }+type HashedIO = StateT HashDir IO -mWithCurrentDirectory :: FileName -> HashedIO p a -> HashedIO p a+mWithCurrentDirectory :: FileName -> HashedIO a -> HashedIO a mWithCurrentDirectory fn j | fn' == fp2fn "" = j | otherwise =@@ -107,7 +97,7 @@ return x where fn' = normPath fn -mInCurrentDirectory :: FileName -> HashedIO p a -> HashedIO p a+mInCurrentDirectory :: FileName -> HashedIO a -> HashedIO a mInCurrentDirectory fn j | fn' == fp2fn "" = j | otherwise = case breakOnDir fn' of@@ -121,10 +111,10 @@ Just h -> inh h $ mInCurrentDirectory fn'' j where fn' = normPath fn -instance ApplyMonad Tree (HashedIO p) where- type ApplyMonadBase (HashedIO p) = IO+instance ApplyMonad Tree HashedIO where+ type ApplyMonadBase HashedIO = IO -instance ApplyMonadTree (HashedIO p) where+instance ApplyMonadTree HashedIO where mDoesDirectoryExist fn = do thing <- identifyThing fn case thing of Just (D,_) -> return True _ -> return False@@ -150,7 +140,7 @@ fail $ "Cannot remove non-empty file "++fn2fp f rmThing f -identifyThing :: FileName -> HashedIO p (Maybe (ObjType,String))+identifyThing :: FileName -> HashedIO (Maybe (ObjType,String)) identifyThing fn | fn' == fp2fn "" = do h <- gets rootHash return $ Just (D, h) | otherwise = case breakOnDir fn' of@@ -161,11 +151,11 @@ Just h -> inh h $ identifyThing fn'' where fn' = normPath fn -makeThing :: FileName -> (ObjType,String) -> HashedIO p ()+makeThing :: FileName -> (ObjType,String) -> HashedIO () makeThing fn (o,h) = mWithCurrentDirectory (superName $ normPath fn) $ seta o (ownName $ normPath fn) h `fmap` readroot >>= writeroot -rmThing :: FileName -> HashedIO p ()+rmThing :: FileName -> HashedIO () rmThing fn = mWithCurrentDirectory (superName $ normPath fn) $ do c <- readroot let c' = filter (\(_,x,_)->x/= ownName (normPath fn)) c@@ -173,13 +163,13 @@ then writeroot c' else fail "obj doesn't exist in rmThing" -readhash :: String -> HashedIO p B.ByteString+readhash :: String -> HashedIO B.ByteString readhash h = do c <- gets cache z <- lift $ unsafeInterleaveIO $ readHashFile c HashedPristineDir h let (_,out) = z return out -withh :: String -> HashedIO p a -> HashedIO p (String,a)+withh :: String -> HashedIO a -> HashedIO (String,a) withh h j = do hd <- get put $ hd { rootHash = h } x <- j@@ -187,24 +177,25 @@ put hd return (h',x) -inh :: String -> HashedIO p a -> HashedIO p a+inh :: String -> HashedIO a -> HashedIO a inh h j = snd `fmap` withh h j -readroot :: HashedIO p [(ObjType, FileName, String)]+readroot :: HashedIO [(ObjType, FileName, String)] readroot = do haveitalready <- peekroot cc <- gets rootHash >>= readdir unless haveitalready $ speculate cc return cc- where speculate :: [(a,b,String)] -> HashedIO q ()+ where speculate :: [(a,b,String)] -> HashedIO () speculate c = do cac <- gets cache mapM_ (\(_,_,z) -> lift $ speculateFileUsingCache cac HashedPristineDir z) c- peekroot :: HashedIO p Bool- peekroot = do HashDir _ c h <- get+ peekroot :: HashedIO Bool+ peekroot = do HashDir c h <- get lift $ peekInCache c HashedPristineDir h -writeroot :: [(ObjType, FileName, String)] -> HashedIO p ()-writeroot c = do h <- writedir c- modify $ \hd -> hd { rootHash = h }+writeroot :: [(ObjType, FileName, String)] -> HashedIO ()+writeroot c = do+ h <- writedir c+ modify $ \hd -> hd { rootHash = h } data ObjType = F | D deriving Eq @@ -224,24 +215,35 @@ seta o f h ((_,f',_):r) | f == f' = (o,f,h):r seta o f h (x:xs) = x : seta o f h xs -readdir :: String -> HashedIO p [(ObjType, FileName, String)]-readdir hash = (parsed . linesPS) `fmap` readhash hash- where parsed (t:n:h:rest) | t == dir = (D, niceps2fn n, BC.unpack h) : parsed rest- | t == file = (F, niceps2fn n, BC.unpack h) : parsed rest- parsed _ = []+readdir :: String -> HashedIO [(ObjType, FileName, String)]+readdir hash = do+ x <- readhash hash+ lift $ debugMessage $ show x+ let r = (parsed . linesPS) x+ lift $ debugMessage $ unlines $ map (\(_,fn,_) -> "DEBUG readdir " ++ hash ++ " entry: " ++ show fn) r+ return r+ where+ parsed (t:n:h:rest) | t == dir = (D, ps2fn n, BC.unpack h) : parsed rest+ | t == file = (F, ps2fn n, BC.unpack h) : parsed rest+ parsed _ = []+ dir :: B.ByteString dir = BC.pack "directory:" file :: B.ByteString file = BC.pack "file:" -writedir :: [(ObjType, FileName, String)] -> HashedIO p String-writedir c = writeHashFile cps- where cps = unlinesPS $ concatMap (\ (o,d,h) -> [showO o,fn2niceps d,BC.pack h]) c++[B.empty]- showO D = dir- showO F = file+writedir :: [(ObjType, FileName, String)] -> HashedIO String+writedir c = do+ lift $ debugMessage $ unlines $ map (\(_,fn,_) -> "DEBUG writedir entry: " ++ show fn) c+ writeHashFile cps+ where+ cps = unlinesPS $ concatMap wr c ++ [B.empty]+ wr (o,d,h) = [showO o,fn2ps d,BC.pack h]+ showO D = dir+ showO F = file -writeHashFile :: B.ByteString -> HashedIO p String+writeHashFile :: B.ByteString -> HashedIO String writeHashFile ps = do c <- gets cache -- pristine files are always compressed lift $ writeFileUsingCache c GzipCompression HashedPristineDir ps@@ -250,13 +252,14 @@ -- | Grab a whole pristine tree from a hash, and, if asked, -- write files in the working copy. copyHashed :: String -> Cache -> WithWorkingDir -> String -> IO ()-copyHashed k c wwd z = void . runStateT cph $ HashDir { permissions = RW, cache = c, rootHash = z }+copyHashed k c wwd z = void . runStateT cph $ HashDir { cache = c, rootHash = z } where cph = do cc <- readroot lift $ tediousSize k (length cc) mapM_ cp cc cp (F,n,h) = do ps <- readhash h lift $ finishedOneIO k (fn2fp n)+ lift $ debugMessage $ "DEBUG copyHashed " ++ show n case wwd of WithWorkingDir -> lift $ writeAtomicFilePS (fn2fp n) ps NoWorkingDir -> ps `seq` return ()@@ -279,7 +282,7 @@ -- Separator "/" is used since this function is used to generate -- zip archives from pristine trees. pathsAndContents :: FilePath -> Cache -> String -> IO [(FilePath,B.ByteString)]-pathsAndContents path c root = evalStateT cph $ HashDir { permissions = RW, cache = c, rootHash = root }+pathsAndContents path c root = evalStateT cph HashDir { cache = c, rootHash = root } where cph = do cc <- readroot pacs <- concat <$> mapM cp cc let current = if path == "." then [] else [(path ++ "/" , B.empty)]@@ -300,7 +303,7 @@ copyPartialHashed c root ff = do createDirectoryIfMissing True (basename $ toFilePath ff) void $ runStateT (cp $ fp2fn $ toFilePath ff)- HashDir { permissions = RW, cache = c,+ HashDir { cache = c, rootHash = root } where basename = reverse . dropWhile ('/' /=) . dropWhile ('/' ==) . reverse cp f = do mt <- identifyThing f@@ -335,5 +338,4 @@ let subs = [ fst $ darcsLocation "" (s, h') | (TreeType, _, s, h') <- x ] hashes = h : [ fst $ darcsLocation "" (s, h') | (_, _, s, h') <- x ] (hashes++) . concat <$> mapM listone subs- hs <- concat <$> mapM listone hashroots- return hs+ concat <$> mapM listone hashroots
@@ -1,776 +0,0 @@--- Copyright (C) 2006-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; if not, write to the Free Software Foundation,--- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.--{-# LANGUAGE CPP, ScopedTypeVariables #-}-module Darcs.Repository.HashedRepo- ( inventoriesDir- , pristineDir- , patchesDir- , hashedInventory- , revertTentativeChanges- , finalizeTentativeChanges- , cleanPristine- , filterDirContents- , cleanInventories- , cleanPatches- , copyPristine- , copyPartialsPristine- , applyToTentativePristine- , addToSpecificInventory- , addToTentativeInventory- , removeFromTentativeInventory- , readRepo- , readTentativeRepo- , readRepoUsingSpecificInventory- , writeAndReadPatch- , writeTentativeInventory- , copyHashedInventory- , readHashedPristineRoot- , pris2inv- , inv2pris- , listInventories- , listInventoriesLocal- , listInventoriesRepoDir- , listPatchesLocalBucketed- , writePatchIfNecessary- , readRepoFromInventoryList- , readPatchIds- , set- , unset- ) where--#include "impossible.h"--import Prelude ()-import Darcs.Prelude--import Control.Arrow ( (&&&) )-import Control.Exception ( catch, IOException )-import Control.Monad ( unless )-import Data.Maybe-import qualified Data.ByteString as B ( null, length, empty ,tail, drop,- ByteString, splitAt )-import qualified Data.ByteString.Char8 as BC ( unpack, dropWhile, break, pack,- ByteString )-import qualified Data.Set as Set-import Darcs.Util.Hash( encodeBase16, Hash(..) )-import Darcs.Util.Tree( treeHash, Tree )-import Darcs.Util.Tree.Hashed( hashedTreeIO, readDarcsHashedNosize,- readDarcsHashed, writeDarcsHashed,- decodeDarcsHash, decodeDarcsSize )-import System.Directory ( createDirectoryIfMissing, getDirectoryContents- , doesFileExist, doesDirectoryExist )-import System.FilePath.Posix( (</>) )-import System.IO.Unsafe ( unsafeInterleaveIO )-import System.IO ( stderr, hPutStrLn )--import Darcs.Util.Printer.Color ( showDoc )-import Darcs.Util.External- ( copyFileOrUrl- , cloneFile- , fetchFilePS- , gzFetchFilePS- , Cachable( Uncachable )- )-import Darcs.Repository.Flags ( Compression, RemoteDarcs, remoteDarcs, WithWorkingDir )-import Darcs.Util.Global ( darcsdir )-import Darcs.Util.Lock- ( writeBinFile- , writeDocBinFile- , writeAtomicFilePS- , appendBinFile- , appendDocBinFile- , removeFileMayNotExist- )-import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, Origin )--import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, patchInfoAndPatch, info,- extractHash, createHashed )-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 ( removeFromPatchSet, slightlyOptimizePatchset )-import Darcs.Patch.Info ( PatchInfo, showPatchInfo, showPatchInfoUI,- readPatchInfo )-import Darcs.Util.Path ( FilePathLike, ioAbsoluteOrRemote, toPath )-import Darcs.Repository.Cache ( Cache(..), fetchFileUsingCache,- speculateFilesUsingCache, writeFileUsingCache,- okayHash, takeHash,- HashedDir(..), hashedDir, peekInCache, bucketFolder )-import Darcs.Repository.HashedIO ( copyHashed, copyPartialsHashed,- cleanHashdir )-import Darcs.Repository.InternalTypes ( Repository(..), extractCache )-import Darcs.Util.File ( withCurrentDirectory )-import Darcs.Patch.Witnesses.Ordered- ( (+<+), FL(..), RL(..), mapRL )-import Darcs.Patch.Witnesses.Sealed ( Sealed(..), seal, unseal, mapSeal )-import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )--import Darcs.Util.ByteString ( gzReadFilePS, dropSpace )-import Darcs.Util.Crypt.SHA256 ( sha256sum )-import Darcs.Util.Printer ( Doc, hcat, (<>), ($$), renderString, renderPS, text,- invisiblePS, RenderMode(..) )-import Darcs.Util.Progress ( beginTedious, endTedious, debugMessage, finishedOneIO )-import Darcs.Util.Workaround ( renameFile )-import Darcs.Repository.Prefs ( globalCacheDir )--makeDarcsdirPath :: String -> String-makeDarcsdirPath name = darcsdir </> name--hashedInventory, hashedInventoryPath :: String-hashedInventory = "hashed_inventory"-hashedInventoryPath = makeDarcsdirPath hashedInventory--tentativeHashedInventory, tentativeHashedInventoryPath :: String-tentativeHashedInventory = "tentative_hashed_inventory"-tentativeHashedInventoryPath = makeDarcsdirPath tentativeHashedInventory--inventoriesDir, inventoriesDirPath :: String-inventoriesDir = "inventories"-inventoriesDirPath = makeDarcsdirPath inventoriesDir--pristineDir, tentativePristinePath, pristineDirPath :: String-tentativePristinePath = makeDarcsdirPath "tentative_pristine"-pristineDir = "pristine.hashed"-pristineDirPath = makeDarcsdirPath pristineDir--patchesDir, patchesDirPath :: String-patchesDir = "patches"-patchesDirPath = makeDarcsdirPath patchesDir--pristineNamePrefix :: String-pristineNamePrefix = "pristine:"--pristineName :: B.ByteString-pristineName = BC.pack pristineNamePrefix---- | 'applyToHashedPristine' takes a root hash, a patch @p@ and attempts to--- apply the patch to the 'Tree' identified by @h@. If we encounter an old,--- size-prefixed pristine, we first convert it to the non-size-prefixed format,--- then apply the patch.-applyToHashedPristine :: (ApplyState p ~ Tree, Patchy p) => String -> p wX wY- -> IO String-applyToHashedPristine h p = applyOrConvertOldPristineAndApply- where- applyOrConvertOldPristineAndApply =- tryApply hash `catch` \(_ :: IOException) -> handleOldPristineAndApply-- hash = decodeDarcsHash $ BC.pack h-- failOnMalformedRoot (SHA256 _) = return ()- failOnMalformedRoot root = fail $ "Cannot handle hash: " ++ show root-- hash2root = BC.unpack . encodeBase16-- tryApply :: Hash -> IO String- tryApply root = do- failOnMalformedRoot root- -- Read a non-size-prefixed pristine, failing if we encounter one.- tree <- readDarcsHashedNosize pristineDirPath root- (_, updatedTree) <- hashedTreeIO (apply p) tree pristineDirPath- return . hash2root $ treeHash updatedTree-- warn = "WARNING: Doing a one-time conversion of pristine format.\n"- ++ "This may take a while. The new format is backwards-compatible."-- handleOldPristineAndApply = do- hPutStrLn stderr warn- inv <- gzReadFilePS hashedInventoryPath- let oldroot = BC.pack $ inv2pris inv- oldrootSizeandHash = (decodeDarcsSize &&& decodeDarcsHash) oldroot- -- Read the old size-prefixed pristine tree- old <- readDarcsHashed pristineDirPath oldrootSizeandHash- -- Write out the pristine tree as a non-size-prefixed pristine.- root <- writeDarcsHashed old pristineDirPath- let newroot = hash2root root- -- Write out the new inventory.- writeDocBinFile hashedInventoryPath $ pris2inv newroot inv- cleanHashdir (Ca []) HashedPristineDir [newroot]- hPutStrLn stderr "Pristine conversion done..."- -- Retry applying the patch, which should now succeed.- tryApply root---- |revertTentativeChanges swaps the tentative and "real" hashed inventory--- files, and then updates the tentative pristine with the "real" inventory--- hash.-revertTentativeChanges :: IO ()-revertTentativeChanges = do- cloneFile hashedInventoryPath tentativeHashedInventoryPath- i <- gzReadFilePS hashedInventoryPath- writeBinFile tentativePristinePath $ pristineNamePrefix ++ inv2pris i---- |finalizeTentativeChanges trys to atomically swap the tentative--- inventory/pristine pointers with the "real" pointers; it first re-reads the--- inventory to optimize it, presumably to take account of any new tags, and--- 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 :: (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- ps <- readTentativeRepo r "."- writeTentativeInventory (extractCache r) compr ps- i <- gzReadFilePS tentativeHashedInventoryPath- p <- gzReadFilePS tentativePristinePath- -- Write out the "optimised" tentative inventory.- writeDocBinFile tentativeHashedInventoryPath $ pris2inv (inv2pris p) i- -- Atomically swap.- renameFile tentativeHashedInventoryPath hashedInventoryPath---- |readHashedPristineRoot attempts to read the pristine hash from the current--- inventory, returning Nothing if it cannot do so.-readHashedPristineRoot :: Repository rt p wR wU wT -> IO (Maybe String)-readHashedPristineRoot (Repo d _ _ _) = withCurrentDirectory d $ do- i <- (Just <$> gzReadFilePS hashedInventoryPath)- `catch` (\(_ :: IOException) -> return Nothing)- return $ inv2pris <$> i---- |cleanPristine removes any obsolete (unreferenced) entries in the pristine--- cache.-cleanPristine :: Repository rt p wR wU wT -> IO ()-cleanPristine r@(Repo d _ _ _) = withCurrentDirectory d $ do- debugMessage "Cleaning out the pristine cache..."- i <- gzReadFilePS hashedInventoryPath- cleanHashdir (extractCache r) HashedPristineDir [inv2pris i]---- |filterDirContents returns the contents of the directory @d@--- except files whose names begin with '.' (directories . and ..,--- hidden files) and files whose names are filtered by the function @f@, if--- @dir@ is empty, no paths are returned.-filterDirContents :: FilePath -> (FilePath -> Bool) -> IO [FilePath]-filterDirContents d f = do- let realPath = makeDarcsdirPath d- exists <- doesDirectoryExist realPath- if exists- then filter (\x -> head x /= '.' && f x) <$>- getDirectoryContents realPath- else return []---- |set converts a list of strings into a set of Char8 ByteStrings for faster--- Set operations.-set :: [String] -> Set.Set BC.ByteString-set = Set.fromList . map BC.pack---- |unset is the inverse of set.-unset :: Set.Set BC.ByteString -> [String]-unset = map BC.unpack . Set.toList---- |cleanInventories removes any obsolete (unreferenced) files in the--- inventories directory.-cleanInventories :: Repository rt p wR wU wT -> IO ()-cleanInventories _ = do- debugMessage "Cleaning out inventories..."- hs <- listInventoriesLocal- fs <- filterDirContents inventoriesDir (const True)- mapM_ (removeFileMayNotExist . (inventoriesDirPath </>))- (unset $ (set fs) `Set.difference` (set hs))---- |specialPatches list of special patch files that may exist in the directory--- _darcs/patches/.-specialPatches :: [FilePath]-specialPatches = ["unrevert", "pending", "pending.tentative"]---- |cleanPatches removes any obsolete (unreferenced) files in the--- patches directory.-cleanPatches :: Repository rt p wR wU wT -> IO ()-cleanPatches _ = do- debugMessage "Cleaning out patches..."- hs <- listPatchesLocal darcsdir- fs <- filterDirContents patchesDir (`notElem` specialPatches)- mapM_ (removeFileMayNotExist . (patchesDirPath </>))- (unset $ (set fs) `Set.difference` (set hs))----- |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 rt p wX wY -> IO FilePath-addToSpecificInventory invPath c compr p = do- let invFile = darcsdir </> invPath- hash <- snd <$> writePatchIfNecessary c compr p- appendDocBinFile invFile $ showPatchInfo $ info p- appendBinFile invFile $ "\nhash: " ++ hash ++ "\n"- return $ darcsdir </> "patches" </> hash--addToTentativeInventory :: RepoPatch p => Cache -> Compression- -> PatchInfoAnd rt p wX wY -> IO FilePath-addToTentativeInventory = addToSpecificInventory tentativeHashedInventory---- | 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- debugMessage $ "Start removeFromTentativeInventory"- allpatches <- readTentativeRepo repo "."- 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.-writeHashFile :: Cache -> Compression -> HashedDir -> Doc -> IO String-writeHashFile c compr subdir d = do- debugMessage $ "Writing hash file to " ++ hashedDir subdir- writeFileUsingCache c compr subdir $ renderPS Standard d---- |readRepo returns the "current" repo patchset.-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 :: (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 :: (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- `catch` \e -> do- hPutStrLn stderr ("Invalid repository: " ++ realdir)- ioError e- return $ unsafeCoerceP ps- where- 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---- |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 :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Cache- -> (Maybe String, [(PatchInfo, String)])- -> IO (SealedPatchSet rt p Origin)-readRepoFromInventoryList cache = parseinvs- where- speculateAndParse h is i = speculate h is >> parse i h-- 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 -> rest :<: i1 `patchInfoAndPatch` p) (rp is1)- (createHashed h1 (const $ speculateAndParse h1 allis i1))- where- 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 -> rest :<: i `patchInfoAndPatch` p)- (rp [(il, hl)])- (createHashed h- (const $ speculateAndParse h (reverse allis) i))- rp ((i, h) : is) =- lift2Sealed (\p rest -> rest :<: i `patchInfoAndPatch` p)- (rp is)- (createHashed h (parse i))-- 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)-- speculate :: String -> [(PatchInfo, String)] -> IO ()- speculate h is = do- already_got_one <- peekInCache cache HashedPatchesDir h- unless already_got_one $- speculateFilesUsingCache cache HashedPatchesDir (map snd is)-- parse :: ReadPatch p => PatchInfo -> String -> IO (Sealed (p wX))- parse i h = do- debugMessage ("Reading patch file: "++ showDoc Encode (showPatchInfoUI i))- (fn, ps) <- fetchFileUsingCache cache HashedPatchesDir h- case readPatch ps of- Just p -> return p- Nothing -> fail $ unlines [ "Couldn't parse file " ++ fn- , "which is patch"- , renderString Encode $ showPatchInfoUI i ]-- parseinvs :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)- => (Maybe String, [(PatchInfo, String)])- -> IO (SealedPatchSet rt p Origin)- parseinvs (Nothing, 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 ts ps-- 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- (Just _, _ : ris0) -> ris0- (Nothing, ris0) -> ris0- (Just _, []) -> bug "inventory without tag!"- Sealed ts <- unseal seal <$>- unsafeInterleaveIO- (case contents of- (Just h', t' : _) -> read_ts t' h'- (Just _, []) -> bug "inventory without tag!"- (Nothing, _) -> return $ seal NilRL)- Sealed ps <- unseal seal <$> unsafeInterleaveIO (read_patches is)- Sealed tag00 <- read_tag tag0- return $ seal $ ts :<: Tagged tag00 (Just h0) ps-- readTaggedInventoryFromHash :: String- -> IO (Maybe String, [(PatchInfo, String)])- readTaggedInventoryFromHash invHash = do- (fileName, pristineAndInventory) <-- fetchFileUsingCache cache HashedInventoriesDir invHash- readInventoryFromContent fileName pristineAndInventory-- lift2Sealed :: (forall wY wZ . q wY wZ -> p wX wY -> r wX wZ)- -> IO (Sealed (p wX))- -> (forall wB . IO (Sealed (q wB)))- -> IO (Sealed (r wX))- lift2Sealed f iox ioy = do- Sealed x <- unseal seal <$> unsafeInterleaveIO iox- Sealed y <- unseal seal <$> unsafeInterleaveIO ioy- return $ seal $ f y x---- |readInventoryPrivate reads the inventory with name @invName@ in @dir@.-readInventoryPrivate :: String -> String- -> IO (Maybe String, [(PatchInfo, String)])-readInventoryPrivate dir invName = do- inv <- skipPristine <$> gzFetchFilePS (dir </> invName) Uncachable- readInventoryFromContent (toPath dir ++ "/" ++ darcsdir ++ invName) inv---- |readInventoryFromContent extracts an inventory from the content of an--- inventory file, who's path is @fileName@.-readInventoryFromContent :: FilePath -> B.ByteString- -> IO (Maybe String, [(PatchInfo, String)])-readInventoryFromContent fileName pristineAndInventory = do- (hash, patchIds) <-- if mbStartingWith == BC.pack "Starting with inventory:"- then let (hash, pids) = BC.break ('\n' ==) $ B.tail pistr- hashStr = BC.unpack hash in- if okayHash hashStr- then return (Just hashStr, pids)- else fail $ "Bad hash in file " ++ fileName- else return (Nothing, inventory)- return (hash, readPatchIds patchIds)- where- inventory = skipPristine pristineAndInventory- (mbStartingWith, pistr) = BC.break ('\n' ==) inventory---- |copyRepo copies the hashed inventory of @repo@ to the repository located at--- @remote@.-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."---- |writeAndReadPatch makes a patch lazy, by writing it out to disk (thus--- forcing it), and then re-reads the patch lazily.-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- where- parse i h = do- debugMessage ("Rereading patch file: "++ showDoc Encode (showPatchInfoUI i))- (fn, ps) <- fetchFileUsingCache c HashedPatchesDir h- case readPatch ps of- Just x -> return x- Nothing -> fail $ unlines [ "Couldn't parse patch file " ++ fn- , "which is"- , renderString Encode $ showPatchInfoUI i]-- readp h i = do Sealed x <- createHashed h (parse i)- return . patchInfoAndPatch i $ unsafeCoerceP x---- | writeTentativeInventory writes @patchSet@ as the tentative inventory.-writeTentativeInventory :: RepoPatch p => Cache -> Compression- -> PatchSet rt p Origin wX -> IO ()-writeTentativeInventory cache compr patchSet = do- debugMessage "in writeTentativeInventory..."- createDirectoryIfMissing False inventoriesDirPath- beginTedious tediousName- hsh <- writeInventoryPrivate $ slightlyOptimizePatchset patchSet- endTedious tediousName- debugMessage "still in writeTentativeInventory..."- case hsh of- Nothing -> writeBinFile (darcsdir </> tentativeHashedInventory) ""- Just h -> do- content <- snd <$> fetchFileUsingCache cache HashedInventoriesDir h- writeAtomicFilePS (darcsdir </> tentativeHashedInventory) content- where- tediousName = "Writing inventory"- writeInventoryPrivate :: RepoPatch p => PatchSet rt p Origin wX- -> IO (Maybe String)- writeInventoryPrivate (PatchSet NilRL NilRL) = return Nothing- 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 xs@(_ :<: Tagged t _ _) x) = do- resthash <- write_ts xs- finishedOneIO tediousName $ fromMaybe "" resthash- inventory <- sequence $ mapRL (writePatchIfNecessary cache compr)- (NilRL :<: t +<+ x)- let inventorylist = hcat (map pihash $ reverse inventory)- inventorycontents =- case resthash of- Just h -> text ("Starting with inventory:\n" ++ h) $$- inventorylist- Nothing -> inventorylist- hash <- writeHashFile cache compr HashedInventoriesDir inventorycontents- return $ Just hash- where- -- | write_ts 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 rt p) Origin wX- -> IO (Maybe String)- 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 rt p wX wY -> IO (PatchInfo, String)-writePatchIfNecessary c compr hp = infohp `seq`- case extractHash hp of- Right h -> return (infohp, h)- Left p -> (\h -> (infohp, h)) <$>- writeHashFile c compr HashedPatchesDir (showPatch p)- where- infohp = info hp---- |pihash takes an info/hash pair, and renders the info, along with the hash--- as a Doc.-pihash :: (PatchInfo, String) -> Doc-pihash (pinf, hash) = showPatchInfo pinf $$ text ("hash: " ++ hash ++ "\n")---- |listInventoriesWith returns a list of the inventories hashes.--- The function @f@ can be readInventoryPrivate or readInventoryLocalPrivate.--- The argument @hashedRepoDir@ is the path to the repository,--- where it's the 'hashed_inventory' file.--- The argument @darcsDir@ is the path to the directory of inventories files.-listInventoriesWith :: (String -> String- -> IO (Maybe String, [(PatchInfo, String)]))- -> String -> String -> IO [String]-listInventoriesWith f darcsDir hashedRepoDir = do- mbStartingWithInv <- getStartingWithHash hashedRepoDir hashedInventory- followStartingWiths mbStartingWithInv- where- getStartingWithHash invDir inv =- fst <$> f invDir inv-- followStartingWiths Nothing = return []- followStartingWiths (Just startingWith) = do- mbNextInv <- getStartingWithHash (darcsDir </> inventoriesDir) startingWith- (startingWith :) <$> followStartingWiths mbNextInv---- |listInventoriesBucketedWith is similar to listInventoriesWith, but--- it read the inventory directory under @darcsDir@ in bucketed format.-listInventoriesBucketedWith :: (String -> String- -> IO (Maybe String, [(PatchInfo, String)]))- -> String -> String -> IO [String]-listInventoriesBucketedWith f darcsDir hashedRepoDir = do- mbStartingWithInv <- getStartingWithHash hashedRepoDir hashedInventory- followStartingWiths mbStartingWithInv- where- getStartingWithHash invDir inv =- fst <$> f invDir inv-- followStartingWiths Nothing = return []- followStartingWiths (Just startingWith) = do- mbNextInv <- getStartingWithHash- (darcsDir </> inventoriesDir </> bucketFolder startingWith) startingWith- (startingWith :) <$> followStartingWiths mbNextInv---- |listInventories returns a list of the inventories hashes.--- This function attempts to retrieve missing inventory files.-listInventories :: IO [String]-listInventories = listInventoriesWith readInventoryPrivate darcsdir darcsdir---- |readInventoryLocalPrivate reads the inventory with name @invName@ in @dir@--- if it exist, otherwise returns an empty inventory.-readInventoryLocalPrivate :: String -> String- -> IO (Maybe String, [(PatchInfo, String)])-readInventoryLocalPrivate dir invName = do- b <- doesFileExist (dir </> invName)- if b then readInventoryPrivate dir invName- else return (Nothing, [])---- |listInventoriesLocal returns a list of the inventories hashes.--- This function does not attempt to retrieve missing inventory files.-listInventoriesLocal :: IO [String]-listInventoriesLocal = listInventoriesWith readInventoryLocalPrivate darcsdir darcsdir---- |listInventoriesRepoDir returns a list of the inventories hashes.--- The argument @repoDir@ is the directory of the repository from which--- we are going to read the "hashed_inventory" file.--- The rest of hashed files are read from the global cache.-listInventoriesRepoDir :: String -> IO [String]-listInventoriesRepoDir repoDir = do- gCacheDir' <- globalCacheDir- let gCacheInvDir = fromJust gCacheDir'- listInventoriesBucketedWith readInventoryLocalPrivate gCacheInvDir (repoDir </> darcsdir)---- |listPatchesLocal returns a list of the patches hashes, extracted--- from inventory files, by following the inventory "chain" of "Starting--- with inventory" hashes. This function does not attempt to download missing--- inventory files.--- The argument @darcsDir@ is the path to the darcs directory (e.g. "_darcs")--- of the repository from which we're going to read the inventories.-listPatchesLocal :: String -> IO [String]-listPatchesLocal darcsDir = do- inventory <- readInventoryPrivate darcsDir hashedInventory- followStartingWiths (fst inventory) (getPatches inventory)- where- followStartingWiths Nothing patches = return patches- followStartingWiths (Just startingWith) patches = do- inv <- readInventoryLocalPrivate (darcsDir </> inventoriesDir) startingWith- (patches++) <$> followStartingWiths (fst inv) (getPatches inv)-- getPatches inv = map snd (snd inv)---- |listPatchesLocalBucketed is similar to listPatchesLocal, but--- it read the inventory directory under @darcsDir@ in bucketed format.-listPatchesLocalBucketed :: String -> String -> IO [String]-listPatchesLocalBucketed darcsDir hashedRepoDir = do- inventory <- readInventoryPrivate hashedRepoDir hashedInventory- followStartingWiths (fst inventory) (getPatches inventory)- where- followStartingWiths Nothing patches = return patches- followStartingWiths (Just startingWith) patches = do- inv <- readInventoryLocalPrivate- (darcsDir </> inventoriesDir </> bucketFolder startingWith) startingWith- (patches++) <$> followStartingWiths (fst inv) (getPatches inv)-- getPatches inv = map snd (snd inv)---- | 'readPatchIds inventory' parses the content of a hashed_inventory file--- after the "pristine:" and "Starting with inventory:" header lines have--- been removed. The second value in the resulting tuples is the file hash--- of the associated patch (the "hash:" line).-readPatchIds :: B.ByteString -> [(PatchInfo, String)]-readPatchIds inv | B.null inv = []-readPatchIds inv = case parseStrictly readPatchInfo inv of- Nothing -> []- Just (pinfo, r) ->- case readHash r of- Nothing -> []- Just (h, r') -> (pinfo, h) : readPatchIds r'- where- readHash :: B.ByteString -> Maybe (String, B.ByteString)- readHash s = let s' = dropSpace s- (l, r) = BC.break ('\n' ==) s'- (kw, h) = BC.break (' ' ==) l in- if kw /= BC.pack "hash:" || B.length h <= 1- then Nothing- else Just (BC.unpack $ B.tail h, r)---- |applyToTentativePristine applies a patch @p@ to the tentative pristine--- tree, and updates the tentative pristine hash-applyToTentativePristine :: (ApplyState p ~ Tree, Patchy p) => p wX wY- -> IO ()-applyToTentativePristine p = do- tentativePristine <- gzReadFilePS tentativePristinePath- -- Extract the pristine hash from the tentativePristine file, using- -- inv2pris (this is valid since we normally just extract the hash from the- -- first line of an inventory file; we can pass in a one-line file that- -- just contains said hash).- let tentativePristineHash = inv2pris tentativePristine- newPristineHash <- applyToHashedPristine tentativePristineHash p- writeDocBinFile tentativePristinePath $- pris2inv newPristineHash tentativePristine---- | copyPristine copies a pristine tree into the current pristine dir,--- and possibly copies a clean working copy.--- The target is read from the passed-in dir/inventory name combination.-copyPristine :: Cache -> String -> String -> WithWorkingDir -> IO ()-copyPristine cache dir iname wwd = do- i <- fetchFilePS (dir ++ "/" ++ iname) Uncachable- debugMessage $ "Copying hashed pristine tree: " ++ inv2pris i- let tediousName = "Copying pristine"- beginTedious tediousName- copyHashed tediousName cache wwd $ inv2pris i- endTedious tediousName---- |copyPartialsPristine copies the pristine entries for a given list of--- filepaths.-copyPartialsPristine :: FilePathLike fp => Cache -> String- -> String -> [fp] -> IO ()-copyPartialsPristine c d iname fps = do- i <- fetchFilePS (d ++ "/" ++ iname) Uncachable- copyPartialsHashed c (inv2pris i) fps---- |pris2inv takes an updated pristine hash and an inventory, and outputs the--- new pristine hash followed by the original inventory (having skipped the old--- inventory hash).-pris2inv :: String -> B.ByteString -> Doc-pris2inv h inv = invisiblePS pristineName <> text h $$- invisiblePS (skipPristine inv)---- |inv2pris takes the content of an inventory, and extracts the corresponding--- pristine hash from the inventory (the hash is prefixed by "pristine:").-inv2pris :: B.ByteString -> String-inv2pris inv = case tryDropPristineName inv of- Just rest -> case takeHash rest of- Just (h, _) -> h- Nothing -> error "Bad hash in inventory!"- Nothing -> sha256sum B.empty---- |skipPristine drops the 'pristine: HASH' prefix line, if present.-skipPristine :: B.ByteString -> B.ByteString-skipPristine ps = case tryDropPristineName ps of- Just rest -> B.drop 1 $ BC.dropWhile (/= '\n') rest- Nothing -> ps---- |tryDropPristineName returns the result of dropping the pristineName from--- the input, if it was present, otherwise it returns Nothing.-tryDropPristineName :: B.ByteString -> Maybe B.ByteString-tryDropPristineName input =- if prefix == pristineName then Just rest else Nothing- where- (prefix, rest) = B.splitAt (B.length pristineName) input
@@ -0,0 +1,229 @@+{-|+License : GPL-2++A set of functions to identify and find Darcs repositories+from a given @URL@ or a given filesystem path.+-}++module Darcs.Repository.Identify+ ( maybeIdentifyRepository+ , identifyRepository+ , identifyRepositoryFor+ , IdentifyRepo(..)+ , findRepository+ , amInRepository+ , amNotInRepository+ , amInHashedRepository+ , seekRepo+ , findAllReposInDir+ ) where++import Prelude ()+import Darcs.Prelude++import Control.Monad ( forM )+import Darcs.Repository.Format ( tryIdentifyRepoFormat+ , readProblem+ , transferProblem+ )+import System.Directory ( doesDirectoryExist+ , setCurrentDirectory+ , createDirectoryIfMissing+ , doesFileExist+ , getDirectoryContents+ )+import System.FilePath.Posix ( (</>) )+import System.IO.Error ( catchIOError )+import Data.Maybe ( fromMaybe )++import Darcs.Repository.Old ( oldRepoFailMsg )+import Darcs.Repository.Flags ( UseCache(..), WorkRepo (..) )+import Darcs.Util.Path+ ( toFilePath+ , ioAbsoluteOrRemote+ , toPath+ )+import Darcs.Util.Exception ( catchall )+import Darcs.Util.URL ( isValidLocalPath )+import Darcs.Util.Workaround+ ( getCurrentDirectory+ )+import Darcs.Repository.Prefs ( getCaches )+import Darcs.Repository.InternalTypes( Repository+ , PristineType(..)+ , mkRepo+ , repoFormat+ , repoPristineType+ )+import Darcs.Util.Global ( darcsdir )++import System.Mem( performGC )++-- | The status of a given directory: is it a darcs repository?+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 rt p wR wU wT)+maybeIdentifyRepository useCache "." =+ do darcs <- doesDirectoryExist darcsdir+ if not darcs+ then return (NonRepository $ "Missing " ++ darcsdir ++ " directory")+ else do+ repoFormatOrError <- tryIdentifyRepoFormat "."+ here <- toPath `fmap` ioAbsoluteOrRemote "."+ case repoFormatOrError of+ Left err -> return $ NonRepository err+ Right rf ->+ case readProblem rf of+ Just err -> return $ BadRepository err+ Nothing -> do pris <- identifyPristine+ cs <- getCaches useCache here+ return $ GoodRepository $ mkRepo here rf pris cs+maybeIdentifyRepository useCache url' =+ do url <- toPath `fmap` ioAbsoluteOrRemote url'+ repoFormatOrError <- tryIdentifyRepoFormat url+ case repoFormatOrError of+ Left e -> return $ NonRepository e+ Right rf -> case readProblem rf of+ Just err -> return $ BadRepository err+ Nothing -> do cs <- getCaches useCache url+ return $ GoodRepository $ mkRepo url rf NoPristine cs++identifyPristine :: IO PristineType+identifyPristine =+ do pristine <- doesDirectoryExist $ darcsdir++"/pristine"+ current <- doesDirectoryExist $ darcsdir++"/current"+ hashinv <- doesFileExist $ darcsdir++"/hashed_inventory"+ case (pristine || current, hashinv) of+ (False, False) -> return NoPristine+ (True, False) -> return PlainPristine+ (False, True ) -> return HashedPristine+ _ -> fail "Multiple pristine trees."++-- | identifyRepository identifies the repo at 'url'. Warning:+-- you have to know what kind of patches are found in that repo.+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+ BadRepository s -> fail s+ NonRepository s -> fail s+ GoodRepository r -> return r++-- | @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 rt p wR wU wT vR vU vT.+ Repository rt p wR wU wT+ -> UseCache+ -> String+ -> IO (Repository rt p vR vU vT)+identifyRepositoryFor source useCache url =+ do target <- identifyRepository useCache url+ case transferProblem (repoFormat target) (repoFormat source) of+ Just e -> fail $ "Incompatibility with repository " ++ url ++ ":\n" ++ e+ Nothing -> return target++amInRepository :: WorkRepo -> IO (Either String ())+amInRepository (WorkRepoDir d) =+ do+ setCurrentDirectory d+ status <- maybeIdentifyRepository YesUseCache "."+ case status of+ GoodRepository _ -> return (Right ())+ BadRepository e -> return (Left $ "While " ++ d ++ " looks like a repository directory, we have a problem with it:\n" ++ e)+ NonRepository _ -> return (Left "You need to be in a repository directory to run this command.")+ `catchIOError`+ \e -> return (Left (show e))+ +amInRepository _ =+ fromMaybe (Left "You need to be in a repository directory to run this command.") <$> seekRepo++amInHashedRepository :: WorkRepo -> IO (Either String ())+amInHashedRepository wd+ = do inrepo <- amInRepository wd+ case inrepo of+ Right _ -> do pristine <- identifyPristine+ case pristine of+ HashedPristine -> return (Right ())+ _ -> return (Left oldRepoFailMsg)+ left -> return left++-- | 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. $+-- The result is:+-- Nothing, if no repository found+-- Just (Left errorMessage), if bad repository found+-- Just (Right ()), if good repository found.+-- WARNING this changes the current directory for good if matchFn succeeds+seekRepo :: IO (Maybe (Either String ()))+seekRepo = getCurrentDirectory >>= helper where+ helper startpwd = do+ status <- maybeIdentifyRepository YesUseCache "."+ case status of+ GoodRepository _ -> return . Just $ Right ()+ BadRepository e -> return . Just $ Left e+ NonRepository _ ->+ do cd <- toFilePath `fmap` getCurrentDirectory+ setCurrentDirectory ".."+ cd' <- toFilePath `fmap` getCurrentDirectory+ if cd' /= cd+ then helper startpwd+ else do setCurrentDirectory startpwd+ return Nothing++-- The performGC in this function is a workaround for a library/GHC bug,+-- http://hackage.haskell.org/trac/ghc/ticket/2924 -- (doesn't seem to be a+-- problem on fast machines, but virtual ones trip this from time to time)+amNotInRepository :: WorkRepo -> IO (Either String ())+amNotInRepository (WorkRepoDir d) = do+ createDirectoryIfMissing False d+ `catchall` (performGC >> createDirectoryIfMissing False d)+ -- note that the above could always fail+ setCurrentDirectory d+ amNotInRepository WorkRepoCurrentDir+amNotInRepository _ = do+ status <- maybeIdentifyRepository YesUseCache "."+ case status of+ GoodRepository _ -> return (Left "You may not run this command in a repository.")+ BadRepository e -> return (Left $ "You may not run this command in a repository.\nBy the way, we have a problem with it:\n" ++ e)+ NonRepository _ -> return (Right ())++findRepository :: WorkRepo -> IO (Either String ())+findRepository workrepo =+ case workrepo of+ WorkRepoPossibleURL d | isValidLocalPath d -> do+ setCurrentDirectory d+ findRepository WorkRepoCurrentDir+ WorkRepoDir d -> do+ setCurrentDirectory d+ findRepository WorkRepoCurrentDir+ _ -> fromMaybe (Right ()) <$> seekRepo+ `catchIOError` \e ->+ return (Left (show e))++-- | @findAllReposInDir topDir@ returns all paths to repositories under @topDir@.+findAllReposInDir :: FilePath -> IO [FilePath]+findAllReposInDir topDir = do+ isDir <- doesDirectoryExist topDir+ if isDir+ then do+ status <- maybeIdentifyRepository NoUseCache topDir+ case status of+ GoodRepository repo+ | HashedPristine <- repoPristineType repo -> return [topDir]+ | otherwise -> 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+ findAllReposInDir path+ return (concat paths)
@@ -1,1092 +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, ScopedTypeVariables, Rank2Types, RankNTypes, PatternGuards #-}--module Darcs.Repository.Internal- ( Repository(..)- , maybeIdentifyRepository- , identifyRepository- , identifyRepositoryFor- , IdentifyRepo(..)- , findRepository- , amInRepository- , amNotInRepository- , amInHashedRepository- , revertRepositoryChanges- , announceMergeConflicts- , setTentativePending- , checkUnrecordedConflicts- , readRepo- , readTentativeRepo- , readRepoUsingSpecificInventory- , prefsUrl- , withRecorded- , withTentative- , tentativelyAddPatch- , tentativelyRemovePatches- , tentativelyRemovePatches_- , tentativelyRemoveFromPending- , tentativelyAddToPending- , tentativelyAddPatch_- , tentativelyAddPatches_- , tentativelyReplacePatches- , finalizeRepositoryChanges- , unrevertUrl- , applyToWorking- , createPristineDirectoryTree- , createPartialsPristineDirectoryTree- , reorderInventory- , cleanRepository- , setScriptsExecutable- , setScriptsExecutablePatches- , UpdatePristine(..)- , MakeChanges(..)- , applyToTentativePristine- , makeNewPending- , seekRepo- , repoPatchType- , repoXor- ) where--import Prelude ()-import Darcs.Prelude--import Darcs.Util.Printer ( putDocLn- , (<+>)- , text- , ($$)- , redText- , putDocLnWith- , ($$)- )-import Darcs.Util.Printer.Color (fancyPrinters)-import Darcs.Util.Crypt.SHA1 ( SHA1, sha1Xor, zero )-import Darcs.Repository.State ( readRecorded- , readWorking- , updateIndex- )-import Darcs.Repository.Pending- ( readPending- , readTentativePending- , writeTentativePending- , readNewPending- , writeNewPending- , pendingName- )-import System.Exit ( exitSuccess )-import Darcs.Repository.ApplyPatches- ( runTolerantly- , runSilently- , runDefault- )--import Darcs.Util.SignalHandler ( withSignalsBlocked )-import Darcs.Repository.Format ( RepoFormat- , RepoProperty( HashedInventory- , NoWorkingDir- )- , tryIdentifyRepoFormat- , formatHas- , readProblem- , transferProblem- )-import System.Directory ( doesDirectoryExist- , setCurrentDirectory- , createDirectoryIfMissing- , doesFileExist- )-import Control.Monad ( when- , unless- , filterM- , void- )--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- , primIsHunk- , primIsBinary- , description- , tryToShrink- , commuteFLorComplain- , commute- , fromPrim- , RepoPatch- , IsRepoType- , Patchy- , merge- , listConflictedFiles- , listTouchedFiles- , WrappedNamed- , commuteRL- , fromPrims- , readPatch- , effect- , invert- , primIsAddfile- , primIsAdddir- , primIsSetpref- , apply- , applyToTree- )--import Darcs.Patch.Dummy ( DummyPatch )--import Darcs.Patch.Apply ( ApplyState )--import Darcs.Patch.Inspect ( PatchInspect )-import Darcs.Patch.Prim ( PrimPatchBase- , PrimOf- , tryShrinkingInverse- , PrimPatch- )-import Darcs.Patch.Bundle ( scanBundle- , makeBundleN- )-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- , removeFromTentativeInventory- , copyPristine- , copyPartialsPristine- , applyToTentativePristine- , addToTentativeInventory- , readRepo- , readTentativeRepo- , readRepoUsingSpecificInventory- , cleanPristine- , cleanInventories- , cleanPatches- )-import qualified Darcs.Repository.Old as Old- ( revertTentativeChanges- , readOldRepo- , oldRepoFailMsg- )-import Darcs.Repository.Flags- ( Compression, Verbosity(..), UseCache(..), UpdateWorking (..), AllowConflicts (..), ExternalMerge (..)- , WorkRepo (..), WithWorkingDir (WithWorkingDir) )-import Darcs.Patch.Witnesses.Eq ( EqCheck(..) )-import Darcs.Patch.Witnesses.Unsafe- ( unsafeCoerceP, unsafeCoercePStart )-import Darcs.Patch.Witnesses.Ordered- ( FL(..)- , RL(..)- , (:\/:)(..)- , (:/\:)(..)- , (:>)(..)- , (+>+)- , (+<+)- , lengthFL- , allFL- , filterOutFLFL- , reverseFL- , mapFL_FL- , concatFL- , reverseRL- , mapRL- )-import Darcs.Patch.Witnesses.Sealed- ( Sealed(Sealed)- , seal- , FlippedSeal(FlippedSeal)- , flipSeal- , mapSeal- )-import Darcs.Patch.Permutations ( commuteWhatWeCanFL- , removeFL- )-import Darcs.Patch.Set ( PatchSet(..)- , SealedPatchSet- , newset2RL- , Origin- )-import Darcs.Patch.Depends ( removeFromPatchSet- , mergeThem- , splitOnTag- )-import Darcs.Patch.Show ( ShowPatch )-import Darcs.Util.Path- ( FilePathLike- , AbsolutePath- , toFilePath- , ioAbsoluteOrRemote- , toPath- , anchorPath- )-import Darcs.Util.Exception ( catchall )-import Darcs.Util.File ( withCurrentDirectory )-import Darcs.Util.Prompt ( promptYorn )-import Darcs.Util.Progress ( debugMessage )-import Darcs.Patch.Progress (progressFL)-import Darcs.Util.URL ( isValidLocalPath )-import Darcs.Util.Workaround- ( getCurrentDirectory- , renameFile- , setExecutable- )-import Darcs.Repository.Prefs ( getCaches )-import Darcs.Util.Lock- ( writeDocBinFile- , removeFileMayNotExist- )-import Darcs.Repository.InternalTypes( Repository(..)- , Pristine(..)- )-import Darcs.Util.Global ( darcsdir )--import System.Mem( performGC )--import Darcs.Util.Tree ( Tree )-import qualified Darcs.Util.Tree as Tree-import Darcs.Repository.PatchIndex ( createOrUpdatePatchIndexDisk, doesPatchIndexExist )-#include "impossible.h"---- | The status of a given directory: is it a darcs repository?-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 rt p wR wU wT)-maybeIdentifyRepository useCache "." =- do darcs <- doesDirectoryExist darcsdir- if not darcs- then return (NonRepository $ "Missing " ++ darcsdir ++ " directory")- else do- repoFormatOrError <- tryIdentifyRepoFormat "."- here <- toPath `fmap` ioAbsoluteOrRemote "."- case repoFormatOrError of- Left err -> return $ NonRepository err- Right rf ->- case readProblem rf of- Just err -> return $ BadRepository err- Nothing -> do pris <- identifyPristine- cs <- getCaches useCache here- return $ GoodRepository $ Repo here rf pris cs-maybeIdentifyRepository useCache url' =- do url <- toPath `fmap` ioAbsoluteOrRemote url'- repoFormatOrError <- tryIdentifyRepoFormat url- case repoFormatOrError of- Left e -> return $ NonRepository e- Right rf -> case readProblem rf of- Just err -> return $ BadRepository err- Nothing -> do cs <- getCaches useCache url- return $ GoodRepository $ Repo url rf NoPristine cs--identifyPristine :: IO Pristine-identifyPristine =- do pristine <- doesDirectoryExist $ darcsdir++"/pristine"- current <- doesDirectoryExist $ darcsdir++"/current"- hashinv <- doesFileExist $ darcsdir++"/hashed_inventory"- case (pristine || current, hashinv) of- (False, False) -> return NoPristine- (True, False) -> return PlainPristine- (False, True ) -> return HashedPristine- _ -> fail "Multiple pristine trees."---- | identifyRepository identifies the repo at 'url'. Warning:--- you have to know what kind of patches are found in that repo.-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- BadRepository s -> fail s- NonRepository s -> fail s- GoodRepository r -> return r---- | @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 rt p wR wU wT vR vU vT. RepoPatch p- => Repository rt p wR wU wT- -> UseCache- -> String- -> 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- Just e -> fail $ "Incompatibility with repository " ++ url ++ ":\n" ++ e- Nothing -> return $ Repo absurl target x c--amInRepository :: WorkRepo -> IO (Either String ())-amInRepository (WorkRepoDir d) = do- setCurrentDirectory d `catchall` fail ("can't set directory to "++d)- status <- maybeIdentifyRepository YesUseCache "."- case status of- GoodRepository _ -> return (Right ())- BadRepository e -> return (Left $ "While " ++ d ++ " looks like a repository directory, we have a problem with it:\n" ++ e)- NonRepository _ -> return (Left "You need to be in a repository directory to run this command.")-amInRepository _ =- fromMaybe (Left "You need to be in a repository directory to run this command.") <$> seekRepo--amInHashedRepository :: WorkRepo -> IO (Either String ())-amInHashedRepository wd- = do inrepo <- amInRepository wd- case inrepo of- Right _ -> do pristine <- identifyPristine- case pristine of- HashedPristine -> return (Right ())- _ -> 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. $--- The result is:--- Nothing, if no repository found--- Just (Left errorMessage), if bad repository found--- Just (Right ()), if good repository found.--- WARNING this changes the current directory for good if matchFn succeeds-seekRepo :: IO (Maybe (Either String ()))-seekRepo = getCurrentDirectory >>= helper where- helper startpwd = do- status <- maybeIdentifyRepository YesUseCache "."- case status of- GoodRepository _ -> return . Just $ Right ()- BadRepository e -> return . Just $ Left e- NonRepository _ ->- do cd <- toFilePath `fmap` getCurrentDirectory- setCurrentDirectory ".."- cd' <- toFilePath `fmap` getCurrentDirectory- if cd' /= cd- then helper startpwd- else do setCurrentDirectory startpwd- return Nothing---- The performGC in this function is a workaround for a library/GHC bug,--- http://hackage.haskell.org/trac/ghc/ticket/2924 -- (doesn't seem to be a--- problem on fast machines, but virtual ones trip this from time to time)-amNotInRepository :: WorkRepo -> IO (Either String ())-amNotInRepository (WorkRepoDir d) = do- createDirectoryIfMissing False d- `catchall` (performGC >> createDirectoryIfMissing False d)- -- note that the above could always fail- setCurrentDirectory d- amNotInRepository WorkRepoCurrentDir-amNotInRepository _ = do- status <- maybeIdentifyRepository YesUseCache "."- case status of- GoodRepository _ -> return (Left "You may not run this command in a repository.")- BadRepository e -> return (Left $ "You may not run this command in a repository.\nBy the way, we have a problem with it:\n" ++ e)- NonRepository _ -> return (Right ())--findRepository :: WorkRepo -> IO (Either String ())-findRepository (WorkRepoPossibleURL d) | isValidLocalPath d =- do setCurrentDirectory d `catchall` fail ("can't set directory to "++d)- findRepository WorkRepoCurrentDir-findRepository (WorkRepoDir d) =- do setCurrentDirectory d `catchall` fail ("can't set directory to "++d)- findRepository WorkRepoCurrentDir-findRepository _ = fromMaybe (Right ()) <$> seekRepo---- TODO: see also Repository.State.readPendingLL ... to be removed after GHC 7.2-readNewPendingLL :: (RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT -> IO (Sealed ((FL p) wT))-readNewPendingLL repo = mapSeal (mapFL_FL fromPrim) `fmap` readNewPending repo----- | @makeNewPending repo YesUpdateWorking pendPs@ verifies that the--- @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 rt p wR wU wT wY. (RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT- -> UpdateWorking- -> FL (PrimOf p) wT wY- -> IO ()-makeNewPending _ NoUpdateWorking _ = return ()-makeNewPending repo@(Repo r _ _ _) YesUpdateWorking origp =- withCurrentDirectory r $- do let newname = pendingName ++ ".new"- debugMessage $ "Writing new pending: " ++ newname- Sealed sfp <- return $ siftForPending origp- writeNewPending repo sfp- cur <- readRecorded repo- Sealed p <- readNewPendingLL repo -- :: IO (Sealed (FL (PrimOf p) wT))- -- We don't ever use the resulting tree.- _ <- catch (applyToTree p cur) $ \(err :: IOException) -> do- let buggyname = pendingName ++ "_buggy"- renameFile newname buggyname- bugDoc $ text ("There was an attempt to write an invalid pending! " ++ show err)- $$ text "If possible, please send the contents of"- <+> text buggyname- $$ text "along with a bug report."- renameFile newname pendingName- debugMessage $ "Finished writing new pending: " ++ newname---- | @siftForPending ps@ simplifies the candidate pending patch @ps@--- through a combination of looking for self-cancellations--- (sequences of patches followed by their inverses), coalescing,--- and getting rid of any hunk/binary patches we can commute out--- the back------ The visual image of sifting can be quite helpful here. We are--- repeatedly tapping (shrinking) the patch sequence and--- shaking it (sift). Whatever falls out is the pending we want--- to keep. We do this until the sequence looks about as clean as--- we can get it-siftForPending :: forall prim wX wY . PrimPatch prim => FL prim wX wY -> Sealed (FL prim wX)-siftForPending simple_ps =- if allFL (\p -> primIsAddfile p || primIsAdddir p) oldps- then seal oldps- else fromJust $ do- Sealed x <- return $ sift NilFL $ reverseFL oldps- return $ case tryToShrink x of- ps | lengthFL ps < lengthFL oldps -> siftForPending ps- | otherwise -> seal ps- where- oldps = fromMaybe simple_ps $ tryShrinkingInverse $ crudeSift simple_ps- -- get rid of any hunk/binary patches that we can commute out the- -- back (ie. we work our way backwards, pushing the patches down- -- to the very end and popping them off; so in (addfile f :> hunk)- -- 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 (ps:<:p) | primIsHunk p || primIsBinary p =- case commuteFLorComplain (p :> sofar) of- Right (sofar' :> _) -> sift sofar' ps- Left _ -> sift (p:>:sofar) ps- sift sofar (ps:<:p) = sift (p:>:sofar) ps--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 :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)- => String- -> 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 rt p wR wU wT -> String-prefsUrl (Repo r _ _ _) = r ++ "/"++darcsdir++"/prefs"--unrevertUrl :: Repository rt p wR wU wT -> String-unrevertUrl (Repo r _ _ _) = r ++ "/"++darcsdir++"/patches/unrevert"--applyToWorking :: (ApplyState (PrimOf p) ~ Tree, RepoPatch p)- => 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) $- withCurrentDirectory r $ if verb == Quiet- then runSilently $ apply patch- else runTolerantly $ apply patch- return (Repo r rf t c)---- | @tentativelyRemoveFromPending p@ is used by Darcs whenever it--- adds a patch to the repository (eg. with apply or record).--- Think of it as one part of transferring patches from pending to--- somewhere else.------ Question (Eric Kow): how do we detect patch equivalence?-tentativelyRemoveFromPending :: forall rt p wR wU wT wX wY. (RepoPatch p)- => Repository rt p wR wU wT- -> UpdateWorking- -> PatchInfoAnd rt p wX wY- -> IO ()-tentativelyRemoveFromPending _ NoUpdateWorking _ = return ()-tentativelyRemoveFromPending repo YesUpdateWorking p = do- Sealed pend <- readTentativePending repo- -- Question (Eric Kow): why does pending being all simple matter for- -- 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 isSimple pend- then crudeSift $ effect p- else effect p- Sealed newpend <- return $ rmpend (progressFL "Removing from pending:" effectp)- (unsafeCoercePStart pend)- writeTentativePending repo (unsafeCoercePStart newpend)- where- -- @rmpend effect pending@ removes as much of @effect@ from @pending@- -- as possible- --- -- Note that @effect@ and @pending@ must start from the same context- -- This is not a bad thing to assume because @effect@ is a patch we want to- -- add to the repository anyway so it'd kind of have to start from wR anyway- --- -- Question (Eric Kow), ok then why not- -- @PatchInfoAnd p wR wY@ in the type signature above?- rmpend :: FL (PrimOf p) wA wB -> FL (PrimOf p) wA wC -> Sealed (FL (PrimOf p) wB)- rmpend NilFL x = Sealed x- rmpend _ NilFL = Sealed NilFL- rmpend (x:>:xs) xys | Just ys <- removeFL x xys = rmpend xs ys- rmpend (x:>:xs) ys =- case commuteWhatWeCanFL (x:>xs) of- a:>x':>b -> case rmpend a ys of- Sealed ys' -> case commute (invert (x':>:b) :> ys') of- Just (ys'' :> _) -> seal ys''- Nothing -> seal $ invert (x':>:b)+>+ys'- -- DJR: I don't think this last case should be- -- reached, but it also shouldn't lead to corruption.---- | 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.------ 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 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 }--decideHashedOrNormal :: Monad m => RepoFormat -> HashedVsOld (m a) -> m a-decideHashedOrNormal rf (HvsO { hashed = h, old = o })- | formatHas HashedInventory rf = h- | otherwise = o--data MakeChanges = MakeChanges | DontMakeChanges deriving ( Eq )--announceMergeConflicts :: (PrimPatch p, PatchInspect p)- => String- -> AllowConflicts- -> ExternalMerge- -> FL p wX wY- -> IO Bool-announceMergeConflicts cmd allowConflicts externalMerge resolved_pw =- case nubSort $ listTouchedFiles resolved_pw of- [] -> return False- cfs -> if allowConflicts `elem` [YesAllowConflicts,YesAllowConflictsAndMark]- || externalMerge /= NoExternalMerge- then do putDocLnWith fancyPrinters $- redText "We have conflicts in the following files:" $$ text (unlines cfs)- return True- else do putDocLnWith fancyPrinters $- redText "There are conflicts in the following files:" $$ text (unlines cfs)- fail $ "Refusing to "++cmd++" patches leading to conflicts.\n"++- "If you would rather apply the patch and mark the conflicts,\n"++- "use the --mark-conflicts or --allow-conflicts options to "++cmd++"\n"++- "These can set as defaults by adding\n"++- " "++cmd++" mark-conflicts\n"++- "to "++darcsdir++"/prefs/defaults in the target repo. "--checkUnrecordedConflicts :: forall rt p wT wY. RepoPatch p- => UpdateWorking- -> 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 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- pend ->- case merge (fromPrims_ pend :\/: fromPrims_ (concatFL $ mapFL_FL effect pc)) of- _ :/\: pend' ->- case listConflictedFiles pend' of- [] -> return False- fs -> do putStrLn ("You have conflicting local changes to:\n"- ++ unwords fs)- confirmed <- promptYorn "Proceed?"- unless confirmed $- do putStrLn "Cancelled."- exitSuccess- return True- fromPrims_ :: FL (PrimOf p) wA wB -> FL p wA wB- fromPrims_ = fromPrims--tentativelyAddPatch :: (RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT- -> Compression- -> Verbosity- -> UpdateWorking- -> PatchInfoAnd rt p wT wY- -> IO (Repository rt p wR wU wY)-tentativelyAddPatch = tentativelyAddPatch_ UpdatePristine--data UpdatePristine = UpdatePristine - | DontUpdatePristine- | DontUpdatePristineNorRevert deriving Eq--tentativelyAddPatches_- :: forall rt p wR wU wT wY- . (RepoPatch p, ApplyState p ~ Tree)- => UpdatePristine- -> Repository rt p wR wU wT- -> Compression- -> Verbosity- -> UpdateWorking- -> 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- tentativelyAddPatches_ up r' compr verb uw ps---- TODO re-add a safety catch for --dry-run? Maybe using a global, like dryRun--- :: Bool, with dryRun = unsafePerformIO $ readIORef ...-tentativelyAddPatch_ :: forall rt p wR wU wT wY- . (RepoPatch p, ApplyState p ~ Tree)- => UpdatePristine- -> Repository rt p wR wU wT- -> Compression- -> Verbosity- -> UpdateWorking- -> 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- decideHashedOrNormal rf HvsO {- hashed = void $ HashedRepo.addToTentativeInventory c compr p,- old = fail Old.oldRepoFailMsg}- when (up == UpdatePristine) $ do debugMessage "Applying to pristine cache..."- applyToTentativePristine r verb p- debugMessage "Updating pending..."- tentativelyRemoveFromPending r uw p- return (Repo dir rf t c)--applyToTentativePristine :: (ApplyState q ~ Tree, Effect q, Patchy q, ShowPatch q, PrimPatchBase q)- => Repository rt p wR wU wT- -> Verbosity- -> q wT wY- -> IO ()-applyToTentativePristine (Repo dir rf _ _) verb p =- withCurrentDirectory dir $- do when (verb == Verbose) $ putDocLn $ text "Applying to pristine..." <+> description p- decideHashedOrNormal rf HvsO {hashed = HashedRepo.applyToTentativePristine p,- old = fail Old.oldRepoFailMsg}---- | @tentativelyAddToPending repo NoDryRun YesUpdateWorking pend ps@--- appends @ps@ to the pending patch.------ It has no effect with @NoUpdateWorking@.------ 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 rt p wR wU wT wX wY. RepoPatch p- => Repository rt p wR wU wT- -> UpdateWorking- -> FL (PrimOf p) wX wY- -> IO ()-tentativelyAddToPending _ NoUpdateWorking _ = return ()-tentativelyAddToPending repo@(Repo dir _ _ _) YesUpdateWorking patch =- withCurrentDirectory dir $ do- Sealed pend <- readTentativePending repo- FlippedSeal newpend_ <- return $- newpend (unsafeCoerceP pend :: FL (PrimOf p) wA wX) patch- writeTentativePending repo (unsafeCoercePStart newpend_)- where- newpend :: FL prim wA wB -> FL prim wB wC -> FlippedSeal (FL prim) wC- newpend NilFL patch_ = flipSeal patch_- newpend p patch_ = flipSeal $ p +>+ patch_---- | setTentativePending is basically unsafe. It overwrites the pending--- state with a new one, not related to the repository state.-setTentativePending :: forall rt p wR wU wT wX wY. RepoPatch p- => Repository rt p wR wU wT- -> UpdateWorking- -> FL (PrimOf p) wX wY- -> IO ()-setTentativePending _ NoUpdateWorking _ = return ()-setTentativePending repo@(Repo dir _ _ _) YesUpdateWorking patch = do- Sealed prims <- return $ siftForPending patch- withCurrentDirectory dir $ writeTentativePending repo (unsafeCoercePStart prims)---- | @prepend repo YesUpdateWorking ps@ prepends @ps@ to the pending patch--- It's used right before removing @ps@ from the repo. This ensures that--- the pending patch can still be applied on top of the recorded state.------ This function is basically unsafe. It overwrites the pending state--- with a new one, not related to the repository state.-prepend :: forall rt p wR wU wT wX wY. RepoPatch p- => Repository rt p wR wU wT- -> UpdateWorking- -> FL (PrimOf p) wX wY- -> IO ()-prepend _ NoUpdateWorking _ = return ()-prepend repo YesUpdateWorking patch = do- Sealed pend <- readTentativePending repo- Sealed newpend_ <- return $ newpend (unsafeCoerceP pend) patch- writeTentativePending repo (unsafeCoercePStart $ crudeSift newpend_)- where- newpend :: FL prim wB wC -> FL prim wA wB -> Sealed (FL prim wA)- newpend NilFL patch_ = seal patch_- newpend p patch_ = seal $ patch_ +>+ p--tentativelyRemovePatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT- -> Compression- -> UpdateWorking- -> FL (PatchInfoAnd rt p) wX wT- -> IO (Repository rt p wR wU wX)-tentativelyRemovePatches = tentativelyRemovePatches_ UpdatePristine--tentativelyRemovePatches_ :: forall rt p wR wU wT wX- . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)- => UpdatePristine- -> Repository rt p wR wU wT- -> Compression- -> UpdateWorking- -> 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..."- prepend repository uw $ effect ps- unless (up == DontUpdatePristineNorRevert) $ removeFromUnrevertContext repository ps- debugMessage "Removing changes from tentative inventory..."- if formatHas HashedInventory rf- then do HashedRepo.removeFromTentativeInventory repository compr ps- when (up == UpdatePristine) $- HashedRepo.applyToTentativePristine $- progressFL "Applying inverse to pristine" $ invert ps- else fail Old.oldRepoFailMsg- return (Repo dir rf t c)---- FIXME this is a rather weird API. If called with a patch that isn't already--- in the repo, it fails with an obscure error from 'commuteToEnd'. It also--- ends up redoing the work that the caller has already done - if it has--- already commuted these patches to the end, it must also know the commuted--- versions of the other patches in the repo.--- |Given a sequence of patches anchored at the end of the current repository,--- actually pull them to the end of the repository by removing any patches--- 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 rt p wR wU wT wX- . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT- -> Compression- -> UpdateWorking- -> Verbosity- -> FL (PatchInfoAnd rt p) wX wT- -> IO ()-tentativelyReplacePatches repository compr uw verb ps =- do let ps' = filterOutFLFL (namedIsInternal . hopefully) ps- repository' <- tentativelyRemovePatches_ DontUpdatePristineNorRevert repository compr uw ps'- mapAdd repository' ps'- where mapAdd :: Repository rt p wM wL wI- -> FL (PatchInfoAnd rt p) wI wJ- -> IO ()- mapAdd _ NilFL = return ()- mapAdd r (a:>:as) =- do r' <- tentativelyAddPatch_ DontUpdatePristine r compr verb uw a- mapAdd r' as---- | Replace the pending patch with the tentative pending.--- If @NoUpdateWorking@, this merely deletes the tentative pending--- without replacing the current one.------ Question (Eric Kow): shouldn't this also delete the tentative--- pending if @YesUpdateWorking@? I'm just puzzled by the seeming--- inconsistency of the @NoUpdateWorking@ doing deletion, but--- @YesUpdateWorking@ not bothering.-finalizePending :: (RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT- -> UpdateWorking- -> IO ()-finalizePending (Repo dir _ _ _) NoUpdateWorking =- withCurrentDirectory dir $ removeFileMayNotExist pendingName-finalizePending repository@(Repo dir _ _ _) updateWorking@YesUpdateWorking =- withCurrentDirectory dir $ do- Sealed tpend <- readTentativePending repository- Sealed new_pending <- return $ siftForPending tpend- makeNewPending repository updateWorking new_pending--finalizeRepositoryChanges :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT- -> UpdateWorking- -> Compression- -> IO ()-finalizeRepositoryChanges repository@(Repo dir rf _ _) updateWorking compr- | formatHas HashedInventory rf =- withCurrentDirectory dir $ do- debugMessage "Finalizing changes..."- withSignalsBlocked $ do- HashedRepo.finalizeTentativeChanges repository compr- finalizePending repository updateWorking- debugMessage "Done finalizing changes..."- doesPatchIndexExist dir >>= (`when` createOrUpdatePatchIndexDisk repository)- updateIndex repository- | otherwise = fail Old.oldRepoFailMsg---- TODO: rename this and document the transaction protocol (revert/finalize)--- clearly.--- |Slightly confusingly named: as well as throwing away any tentative--- changes, revertRepositoryChanges also re-initialises the tentative state.--- It's therefore used before makign any changes to the repo.-revertRepositoryChanges :: RepoPatch p- => Repository rt p wR wU wT- -> UpdateWorking- -> IO ()-revertRepositoryChanges r@(Repo dir rf _ _) uw =- withCurrentDirectory dir $- do removeFileMayNotExist (pendingName ++ ".tentative")- Sealed x <- readPending r- setTentativePending r uw x- when (uw == NoUpdateWorking) $ removeFileMayNotExist pendingName- decideHashedOrNormal rf HvsO { hashed = HashedRepo.revertTentativeChanges,- old = Old.revertTentativeChanges }--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))- remove_from_unrevert_context_ bundle- where unrevert_impossible =- do confirmed <- promptYorn "This operation will make unrevert impossible!\nProceed?"- if confirmed then removeFileMayNotExist (unrevertUrl repository)- else fail "Cancelled."- 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 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..."- debugMessage $ "Removing "++ show (lengthFL ps) ++- " patches in removeFromUnrevertContext!"- ref <- readTentativeRepo repository- let withSinglet :: Sealed (FL ppp wXxx)- -> (forall wYyy . ppp wXxx wYyy -> IO ()) -> IO ()- withSinglet (Sealed (x :>: NilFL)) j = j x- withSinglet _ _ = return ()- withSinglet (mergeThem ref bundle) $ \h_us ->- case commuteRL (reverseFL ps :> h_us) of- Nothing -> unrevert_impossible- Just (us' :> _) ->- case removeFromPatchSet ps ref of- Nothing -> unrevert_impossible- Just common ->- do debugMessage "Have now found the new context..."- bundle' <- makeBundleN Nothing common (hopefully us':>:NilFL)- writeDocBinFile (unrevertUrl repository) bundle'- debugMessage "Done adjusting the context of the unrevert changes!"--cleanRepository :: RepoPatch p => Repository rt p wR wU wT -> IO ()-cleanRepository repository@(Repo _ rf _ _) =- decideHashedOrNormal rf- HvsO { hashed = cleanHashedRepo repository,- old = fail Old.oldRepoFailMsg}- where- cleanHashedRepo r = do- HashedRepo.cleanPristine r- HashedRepo.cleanInventories r- HashedRepo.cleanPatches r----- | 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 rt p wR wU wT -> FilePath -> WithWorkingDir -> IO ()-createPristineDirectoryTree (Repo r rf _ c) reldir wwd- | formatHas HashedInventory rf =- do createDirectoryIfMissing True reldir- withCurrentDirectory reldir $ HashedRepo.copyPristine c r (darcsdir++"/hashed_inventory") wwd- | otherwise = fail Old.oldRepoFailMsg---- fp below really should be FileName--- | Used by the commands dist and diff-createPartialsPristineDirectoryTree :: (FilePathLike fp, RepoPatch p)- => Repository rt p wR wU wT- -> [fp]- -> FilePath- -> IO ()-createPartialsPristineDirectoryTree (Repo r rf _ c) prefs dir- | formatHas HashedInventory rf =- do createDirectoryIfMissing True dir- withCurrentDirectory dir $- HashedRepo.copyPartialsPristine c r (darcsdir++"/hashed_inventory") prefs- | otherwise = fail Old.oldRepoFailMsg--withRecorded :: RepoPatch p- => Repository rt p wR wU wT- -> ((AbsolutePath -> IO a) -> IO a)- -> (AbsolutePath -> IO a)- -> IO a-withRecorded repository mk_dir f- = mk_dir $ \d -> do createPristineDirectoryTree repository (toFilePath d) WithWorkingDir- f d--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-withTentative (Repo dir rf _ c) mk_dir f- | formatHas HashedInventory rf =- mk_dir $ \d -> do HashedRepo.copyPristine- c- dir- (darcsdir++"/tentative_pristine")- WithWorkingDir- f d-withTentative repository@(Repo dir _ _ _) mk_dir f =- withRecorded repository mk_dir $ \d ->- do Sealed ps <- read_patches (dir ++ "/"++darcsdir++"/tentative_pristine")- runDefault $ apply ps- f d- where read_patches :: FilePath -> IO (Sealed (FL p wX))- read_patches fil = do ps <- B.readFile fil- return $ fromMaybe (seal NilFL) $ readPatch ps---- | Sets scripts in or below the current directory executable.--- A script is any file that starts with the bytes '#!'.--- This is used for --set-scripts-executable.-setScriptsExecutable_ :: PatchInspect p => Maybe (p wX wY) -> IO ()-setScriptsExecutable_ pw = do- debugMessage "Making scripts executable"- tree <- readWorking- paths <- case pw of- Just ps -> filterM doesFileExist $ listTouchedFiles ps- Nothing -> return [ anchorPath "." p | (p, Tree.File _) <- Tree.list tree ]- let setExecutableIfScript f =- do contents <- B.readFile f- when (BC.pack "#!" `B.isPrefixOf` contents) $ do- debugMessage ("Making executable: " ++ f)- setExecutable f True- mapM_ setExecutableIfScript paths--setScriptsExecutable :: IO ()-setScriptsExecutable = setScriptsExecutable_ (Nothing :: Maybe (FL DummyPatch wX wY))--setScriptsExecutablePatches :: PatchInspect p => p wX wY -> IO ()-setScriptsExecutablePatches = setScriptsExecutable_ . Just----- | Writes out a fresh copy of the inventory that minimizes the--- amount of inventory that need be downloaded when people pull from--- the repository.------ Specifically, it breaks up the inventory on the most recent tag.--- This speeds up most commands when run remotely, both because a--- smaller file needs to be transfered (only the most recent--- inventory). It also gives a guarantee that all the patches prior--- 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 :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wR- -> Compression- -> UpdateWorking- -> Verbosity- -> IO ()-reorderInventory repository@(Repo _ rf _ _) compr uw verb =- decideHashedOrNormal rf HvsO {- hashed = do- debugMessage "Reordering the inventory."- 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 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.- case filter isTag $ mapRL info $ newset2RL ps of- [] -> ps- (lt:_) -> - -- Take the most recent tag, and split the repository in,- -- the clean PatchSet "up to" the tag (ts), and a RL of- -- patches after the tag (r).- case splitOnTag lt ps of- 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
@@ -14,22 +14,31 @@ -- along with this program; if not, write to the Free Software Foundation, -- Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -{-# LANGUAGE CPP #-}---module Darcs.Repository.InternalTypes ( Repository(..), Pristine(..)- , extractCache, modifyCache+module Darcs.Repository.InternalTypes ( Repository, PristineType(..)+ , repoCache, modifyCache+ , repoPatchType+ , repoFormat+ , repoLocation+ , withRepoLocation+ , repoPristineType+ , coerceR+ , coerceU+ , coerceT+ , mkRepo ) where import Prelude () import Darcs.Prelude +import Data.Coerce ( coerce ) import Data.List ( nub, sortBy ) import Darcs.Repository.Cache ( Cache (..) , compareByLocality ) import Darcs.Repository.Format ( RepoFormat )-import Darcs.Patch ( RepoPatch, RepoType )+import Darcs.Patch ( RepoType )+import Darcs.Patch.Type ( PatchType(..) )+import Darcs.Util.File ( withCurrentDirectory ) -data Pristine+data PristineType = NoPristine | PlainPristine | HashedPristine@@ -42,14 +51,41 @@ -- and the tentative state, which represents work in progress that will -- eventually become the new recorded state unless something goes wrong. data Repository (rt :: RepoType) (p :: * -> * -> *) wRecordedstate wUnrecordedstate wTentativestate =- Repo !String !RepoFormat !Pristine Cache deriving ( Show )+ Repo !String !RepoFormat !PristineType Cache deriving ( Show ) -extractCache :: Repository rt p wR wU wT -> Cache-extractCache (Repo _ _ _ c) = c+repoLocation :: Repository rt p wR wU wT -> String+repoLocation (Repo loc _ _ _) = loc +withRepoLocation :: Repository rt p wR wU wT -> IO a -> IO a+withRepoLocation repo = withCurrentDirectory (repoLocation repo)++repoFormat :: Repository rt p wR wU wT -> RepoFormat+repoFormat (Repo _ fmt _ _) = fmt++repoPristineType :: Repository rt p wR wU wT -> PristineType+repoPristineType (Repo _ _ pr _) = pr++repoCache :: Repository rt p wR wU wT -> Cache+repoCache (Repo _ _ _ c) = c+ -- | 'modifyCache' @repository function@ modifies the cache of -- @repository@ with @function@, remove duplicates and sort the results with 'compareByLocality'.-modifyCache :: forall rt p wR wU wT . (RepoPatch p) => Repository rt p wR wU wT -> (Cache -> Cache) -> Repository rt p wR wU wT+modifyCache :: forall rt p wR wU wT . 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)++repoPatchType :: Repository rt p wR wU wT -> PatchType rt p+repoPatchType _ = PatchType++coerceR :: Repository rt p wR wU wT -> Repository rt p wR' wU wT+coerceR = coerce++coerceU :: Repository rt p wR wU wT -> Repository rt p wR wU' wT+coerceU = coerce++coerceT :: Repository rt p wR wU wT -> Repository rt p wR wU wT'+coerceT = coerce++mkRepo :: String -> RepoFormat -> PristineType -> Cache -> Repository rt p wR wU wT+mkRepo = Repo
@@ -0,0 +1,239 @@+module Darcs.Repository.Inventory+ ( Inventory(..)+ , HeadInventory+ , InventoryEntry+ , ValidHash(..)+ , InventoryHash+ , PatchHash+ , PristineHash+ , inventoryPatchNames+ , parseInventory+ , showInventory+ , showInventoryPatches+ , showInventoryEntry+ , emptyInventory+ , pokePristineHash+ , peekPristineHash+ , skipPristineHash+ , pristineName+ -- properties+ , prop_inventoryParseShow+ , prop_peekPokePristineHash+ , prop_skipPokePristineHash+ ) where++import Prelude ()+import Darcs.Prelude hiding ( take )++import Control.Applicative ( optional, many )+import Control.Monad ( guard )++import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC++import Darcs.Patch.Info ( PatchInfo, showPatchInfo, readPatchInfo )+import Darcs.Patch.ReadMonads+ ( ParserM, parseStrictly, string, skipSpace, take, takeTillChar )+import Darcs.Patch.Show ( ShowPatchFor(..) )+import Darcs.Repository.Cache ( okayHash )+import Darcs.Util.Hash ( sha256sum )+import Darcs.Util.Printer+ ( Doc, (<>), (<+>), ($$), hcat, text, invisiblePS, packedString, renderPS )++-- * Hash validation++-- TODO the ValidHash class and the newtypes for the various hashes+-- really don't belong here. They should be moved to D.R.Cache or+-- perhaps a separate module. Also, the validation should be extended+-- see D.R.Cache.checkHash.++class ValidHash a where+ getValidHash :: a -> String+ mkValidHash :: String -> a++newtype InventoryHash = InventoryHash String+ deriving (Eq, Show)++instance ValidHash InventoryHash where+ getValidHash (InventoryHash h) = h+ mkValidHash s+ | okayHash s = InventoryHash s+ | otherwise = error "Bad inventory hash!"++newtype PatchHash = PatchHash String+ deriving (Eq, Show)++instance ValidHash PatchHash where+ getValidHash (PatchHash h) = h+ mkValidHash s+ | okayHash s = PatchHash s+ | otherwise = error "Bad patch hash!"++newtype PristineHash = PristineHash String+ deriving (Eq, Show)++instance ValidHash PristineHash where+ getValidHash (PristineHash h) = h+ mkValidHash s+ | okayHash s = PristineHash s+ | otherwise = error "Bad pristine hash!"++-- * Inventories++-- Note: this type and the commented out parser combinators for it+-- aren't actually used (except for testing). They are left here to+-- serve as documentation for the API we would like to use but won't+-- because of efficiency: we want to be able to access the pristine+-- hash with forcing a complete parse of the head inventory. Thus we+-- retain the lower-level peek/poke/skip API for the pristine hash.+type HeadInventory = (PristineHash, Inventory)++data Inventory = Inventory+ { inventoryParent :: Maybe InventoryHash+ , inventoryPatches :: [InventoryEntry]+ } deriving (Eq, Show)++-- The 'String' is the (hashed) patch filename.+type InventoryEntry = (PatchInfo, PatchHash)++inventoryPatchNames :: Inventory -> [String]+inventoryPatchNames = map (getValidHash . snd) . inventoryPatches++emptyInventory :: Inventory+emptyInventory = Inventory Nothing []++-- * Parsing++{-+parseHeadInventory :: B.ByteString -> Maybe HeadInventory+parseHeadInventory = fmap fst . parse pHeadInv+-}++parseInventory :: B.ByteString -> Maybe Inventory+parseInventory = fmap fst . parseStrictly pInv++{-+pHeadInv :: ParserM m => m HeadInventory+pHeadInv = (,) <$> pInvPristine <*> pInv++pInvPristine :: ParserM m => m ValidHash+pInvPristine = do+ string pristineName+ skipSpace+ pHash+-}++pInv :: ParserM m => m Inventory+pInv = Inventory <$> pInvParent <*> pInvPatches++pInvParent :: ParserM m => m (Maybe InventoryHash)+pInvParent = optional $ do+ string parentName+ skipSpace+ pHash++pHash :: (ParserM m, ValidHash h) => m h+pHash = do+ hash <- BC.unpack <$> pLine+ guard (okayHash hash)+ return (mkValidHash hash)++pLine :: ParserM m => m B.ByteString+pLine = takeTillChar '\n' <* take 1++pInvPatches :: ParserM m => m [InventoryEntry]+pInvPatches = many pInvEntry++pInvEntry :: ParserM m => m InventoryEntry+pInvEntry = do+ info <- readPatchInfo+ skipSpace+ string hashName+ skipSpace+ hash <- pHash+ return (info, hash)++-- * Showing++showInventory :: Inventory -> Doc+showInventory inv =+ showParent (inventoryParent inv) <>+ showInventoryPatches (inventoryPatches inv)++showInventoryPatches :: [InventoryEntry] -> Doc+showInventoryPatches = hcat . map showInventoryEntry++showInventoryEntry :: InventoryEntry -> Doc+showInventoryEntry (pinf, hash) =+ showPatchInfo ForStorage pinf $$+ packedString hashName <+> text (getValidHash hash) <> packedString newline++showParent :: Maybe InventoryHash -> Doc+showParent (Just (InventoryHash hash)) =+ packedString parentName $$ text hash <> packedString newline+showParent Nothing = mempty++-- * Accessing the pristine hash++-- | Replace the pristine hash at the start of a raw, unparsed 'HeadInventory'+-- or add it if none is present.+pokePristineHash :: String -> B.ByteString -> Doc+pokePristineHash h inv =+ invisiblePS pristineName <> text h $$ invisiblePS (skipPristineHash inv)++takeHash :: B.ByteString -> Maybe (String, B.ByteString)+takeHash input = do+ let (hline,rest) = BC.breakSubstring newline input+ let hash = BC.unpack hline+ guard $ okayHash hash+ return (hash, rest)++peekPristineHash :: B.ByteString -> String+peekPristineHash inv =+ case tryDropPristineName inv of+ Just rest ->+ case takeHash rest of+ Just (h, _) -> h+ Nothing -> error $ "Bad hash in inventory!"+ Nothing -> sha256sum B.empty++-- |skipPristineHash drops the 'pristine: HASH' prefix line, if present.+skipPristineHash :: B.ByteString -> B.ByteString+skipPristineHash ps =+ case tryDropPristineName ps of+ Just rest -> B.drop 1 $ BC.dropWhile (/= '\n') rest+ Nothing -> ps++tryDropPristineName :: B.ByteString -> Maybe B.ByteString+tryDropPristineName input =+ if prefix == pristineName then Just rest else Nothing+ where+ (prefix, rest) = B.splitAt (B.length pristineName) input++-- * Key phrases++pristineName :: B.ByteString+pristineName = BC.pack "pristine:"++parentName :: B.ByteString+parentName = BC.pack "Starting with inventory:"++hashName :: B.ByteString+hashName = BC.pack "hash:"++newline :: B.ByteString+newline = BC.pack "\n"++-- * Properties++prop_inventoryParseShow :: Inventory -> Bool+prop_inventoryParseShow inv =+ Just inv == parseInventory (renderPS (showInventory inv))++prop_peekPokePristineHash :: (PristineHash, B.ByteString) -> Bool+prop_peekPokePristineHash (PristineHash hash, raw) =+ hash == peekPristineHash (renderPS (pokePristineHash hash raw))++prop_skipPokePristineHash :: (PristineHash, B.ByteString) -> Bool+prop_skipPokePristineHash (PristineHash hash, raw) =+ raw == skipPristineHash (renderPS (pokePristineHash hash raw))
@@ -16,15 +16,18 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, ScopedTypeVariables, Rank2Types, RankNTypes #-} {-# LANGUAGE ForeignFunctionInterface #-} module Darcs.Repository.Job ( RepoJob(..)+ , IsPrimV1(..) , withRepoLock+ , withOldRepoLock , withRepoLockCanFail , withRepository- , withRepositoryDirectory+ , withRepositoryLocation+ , checkRepoIsNoRebase+ , withUMaskFlag ) where import Prelude ()@@ -35,12 +38,15 @@ import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.V1 ( RepoPatchV1 ) import Darcs.Patch.V2 ( RepoPatchV2 )-import Darcs.Patch.Prim.V1 ( Prim )+import qualified Darcs.Patch.V1.Prim as V1 ( Prim(..) )+import qualified Darcs.Patch.V2.Prim as V2 ( Prim(..) ) import Darcs.Patch.Prim ( PrimOf )+import Darcs.Patch.Prim.V1 ( Prim ) import Darcs.Patch.RepoPatch ( RepoPatch ) import Darcs.Patch.RepoType ( RepoType(..), SRepoType(..), IsRepoType , RebaseType(..), SRebaseType(..), IsRebaseType+ , singletonRepoType ) import Darcs.Repository.Flags@@ -49,15 +55,18 @@ import Darcs.Repository.Format ( RepoProperty( Darcs2 , RebaseInProgress+ , HashedInventory ) , formatHas , writeProblem )-import Darcs.Repository.Internal- ( identifyRepository- , revertRepositoryChanges+import Darcs.Repository.Identify ( identifyRepository )+import Darcs.Repository.Hashed( revertRepositoryChanges )+import Darcs.Repository.InternalTypes+ ( Repository+ , repoFormat+ , repoLocation )-import Darcs.Repository.InternalTypes ( Repository(..) ) import Darcs.Repository.Rebase ( RebaseJobFlags , startRebaseJob@@ -70,6 +79,7 @@ import Control.Monad ( when ) import Control.Exception ( bracket_, finally )+import Data.Coerce ( coerce ) import Data.List ( intercalate ) import Foreign.C.String ( CString, withCString )@@ -78,8 +88,6 @@ import Darcs.Util.Tree ( Tree ) -#include "impossible.h"- getUMask :: UMask -> Maybe String getUMask (YesUMask s) = Just s getUMask NoUMask = Nothing@@ -114,23 +122,23 @@ = -- |The most common @RepoJob@; the underlying action can accept any patch type that -- a darcs repository may use.- RepoJob (forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ RepoJob (forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wR -> IO a) -- |A job that only works on darcs 1 patches- | V1Job (forall wR wU . Repository ('RepoType 'NoRebase) (RepoPatchV1 Prim) wR wU wR -> IO a)+ | V1Job (forall wR wU . Repository ('RepoType 'NoRebase) (RepoPatchV1 V1.Prim) wR wU wR -> IO a) -- |A job that only works on darcs 2 patches- | V2Job (forall rt wR wU . Repository rt (RepoPatchV2 Prim) wR wU wR -> IO a)+ | V2Job (forall rt wR wU . IsRepoType rt => Repository rt (RepoPatchV2 V2.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 rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, PrimOf p ~ Prim)+ | PrimV1Job (forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, IsPrimV1 (PrimOf p)) => 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 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)+ | RebaseAwareJob RebaseJobFlags (forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wR -> IO a)+ | RebaseJob RebaseJobFlags (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a)+ | StartRebaseJob RebaseJobFlags (forall p wR wU . (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO a) onRepoJob :: RepoJob 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)@@ -145,38 +153,46 @@ -- | apply a given RepoJob to a repository in the current working directory withRepository :: UseCache -> RepoJob a -> IO a-withRepository useCache = withRepositoryDirectory useCache "."+withRepository useCache = withRepositoryLocation 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)+ RepoV1 :: RepoPatchType (RepoPatchV1 V1.Prim)+ RepoV2 :: RepoPatchType (RepoPatchV2 V2.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+ IsTree :: (ApplyState p ~ Tree) => IsTree p checkTree :: RepoPatchType p -> IsTree p checkTree RepoV1 = IsTree checkTree RepoV2 = IsTree +class ApplyState p ~ Tree => IsPrimV1 p where+ toPrimV1 :: p wX wY -> Prim wX wY+instance IsPrimV1 V1.Prim where+ toPrimV1 = V1.unPrim+instance IsPrimV1 V2.Prim where+ toPrimV1 = V2.unPrim+ -- | 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+ UsesPrimV1 :: (ApplyState p ~ Tree, IsPrimV1 (PrimOf p)) => 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@(Repo _ rf _ _) <- identifyRepository useCache url+withRepositoryLocation :: UseCache -> String -> RepoJob a -> IO a+withRepositoryLocation useCache url repojob = do+ repo <- identifyRepository useCache url let+ rf = repoFormat repo startRebase = case repojob of StartRebaseJob {} -> True@@ -204,13 +220,17 @@ 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+ => RepoPatchType p+ -> SRepoType rt+ -> Repository rtDummy pDummy wR wU wR+ -> RepoJob a+ -> IO a+runJob patchType (SRepoType isRebase) repo 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+ therepo = coerce repo :: Repository rt p wR wU wR patchTypeString :: String patchTypeString =@@ -230,7 +250,8 @@ [] -> "" _ -> " " ++ intercalate "+" repoAttributes - debugMessage $ "Identified " ++ patchTypeString ++ repoAttributesString ++ " repo: " ++ dir+ debugMessage $ "Identified " ++ patchTypeString ++ repoAttributesString +++ " repo: " ++ repoLocation repo case repojob of RepoJob job ->@@ -284,24 +305,47 @@ -- taking a lock withRepoLock :: DryRun -> UseCache -> UpdateWorking -> UMask -> RepoJob a -> IO a withRepoLock dry useCache uw um repojob =- withRepository useCache $ onRepoJob repojob $ \job repository@(Repo _ rf _ _) ->- do maybe (return ()) fail $ writeProblem rf+ withRepository useCache $ onRepoJob repojob $ \job repository ->+ do maybe (return ()) fail $ writeProblem (repoFormat repository) let name = "./"++darcsdir++"/lock" withUMaskFlag um $ if dry == YesDryRun then job repository else withLock name (revertRepositoryChanges repository uw >> job repository) --- | apply a given RepoJob to a repository in the current working directory,--- taking a lock. If lock not takeable, do nothing.-withRepoLockCanFail :: UseCache -> UpdateWorking -> UMask -> RepoJob () -> IO ()-withRepoLockCanFail useCache uw um repojob =- withRepository useCache $ onRepoJob repojob $ \job repository@(Repo _ rf _ _) ->- do maybe (return ()) fail $ writeProblem rf+-- | run a lock-taking job in an old-fashion repository.+-- only used by `darcs optimize upgrade`.+withOldRepoLock :: RepoJob a -> IO a+withOldRepoLock repojob =+ withRepository NoUseCache $ onRepoJob repojob $ \job repository ->+ do let name = "./"++darcsdir++"/lock"+ withLock name $ job repository++-- | Apply a given RepoJob to a repository in the current working directory,+-- taking a lock. If lock not takeable, do nothing. If old-fashioned+-- repository, do nothing. The job must not touch pending or pending.tentative,+-- because there is no call to revertRepositoryChanges. This entry point is+-- currently only used for attemptCreatePatchIndex.+withRepoLockCanFail :: UseCache -> RepoJob () -> IO ()+withRepoLockCanFail useCache repojob =+ withRepository useCache $ onRepoJob repojob $ \job repository ->+ let rf = repoFormat repository in+ if formatHas HashedInventory rf then do+ maybe (return ()) fail $ writeProblem rf let name = "./"++darcsdir++"/lock"- withUMaskFlag um $ do- eitherDone <- withLockCanFail name (revertRepositoryChanges repository uw >> job repository)- case eitherDone of- Left _ -> debugMessage "Lock could not be obtained, not doing the job."- Right _ -> return ()+ eitherDone <- withLockCanFail name (job repository)+ case eitherDone of+ Left _ -> debugMessage "Lock could not be obtained, not doing the job."+ Right _ -> return ()+ else debugMessage "Not doing the job because this is an old-fashioned repository." +-- | If the 'RepoType' of the given repo indicates that we have 'NoRebase',+-- then 'Just' the repo with the refined type, else 'Nothing'.+-- NB The amount of types we have to import to make this simple check is ridiculous!+checkRepoIsNoRebase :: forall rt p wR wU wT. IsRepoType rt+ => Repository rt p wR wU wT+ -> Maybe (Repository ('RepoType 'NoRebase) p wR wU wT)+checkRepoIsNoRebase repo =+ case singletonRepoType :: SRepoType rt of+ SRepoType SNoRebase -> Just repo+ SRepoType SIsRebase -> Nothing
@@ -15,12 +15,9 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, ScopedTypeVariables, MultiParamTypeClasses, FlexibleInstances, Rank2Types #-}- module Darcs.Repository.Match ( getNonrangeMatch- , getFirstMatch , getOnePatchset ) where @@ -29,7 +26,6 @@ import Darcs.Patch.Match ( getNonrangeMatchS- , getFirstMatchS , nonrangeMatcherIsTag , getMatchingTag , matchAPatchset@@ -49,15 +45,14 @@ import Darcs.Repository.Flags ( WithWorkingDir (WithWorkingDir) ) import Darcs.Repository.ApplyPatches ( DefaultIO, runDefault )-import Darcs.Repository.Internal- ( Repository, readRepo, createPristineDirectoryTree )+import Darcs.Repository.InternalTypes ( Repository )+import Darcs.Repository.Hashed+ ( readRepo, createPristineDirectoryTree ) import Darcs.Util.Tree ( Tree ) import Darcs.Util.Path ( toFilePath ) -#include "impossible.h"- getNonrangeMatch :: (ApplyMonad (ApplyState p) DefaultIO, IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> [MatchFlag]@@ -68,13 +63,7 @@ | otherwise -> fail "Index range is not allowed for this command." _ -> getNonrangeMatchS fs -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 :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+getOnePatchset :: (IsRepoType rt, RepoPatch p) => Repository rt p wR wU wT -> [MatchFlag] -> IO (SealedPatchSet rt p Origin)@@ -89,7 +78,7 @@ context_f (Context f:_) = f context_f (_:xs) = context_f xs -withRecordedMatch :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+withRecordedMatch :: (IsRepoType rt, RepoPatch p) => Repository rt p wR wU wT -> (PatchSet rt p Origin wR -> DefaultIO ()) -> IO ()
@@ -21,15 +21,34 @@ module Darcs.Repository.Merge ( tentativelyMergePatches , considerMergeToWorking+ , announceMergeConflicts ) where import Prelude () import Darcs.Prelude -import Control.Monad ( when )-import Darcs.Util.Tree( Tree )+import Control.Monad ( when, unless )+import Data.List.Ordered ( nubSort )+import System.Exit ( exitSuccess ) +import Darcs.Util.Tree( Tree ) import Darcs.Util.External ( backupByCopying )++import Darcs.Patch+ ( RepoPatch, IsRepoType, PrimOf, merge, listTouchedFiles+ , fromPrims, effect, WrappedNamed+ , listConflictedFiles )+import Darcs.Patch.Prim ( PrimPatch )+import Darcs.Patch.Apply ( ApplyState )+import Darcs.Patch.Depends( merge2FL )+import Darcs.Patch.Named.Wrapped ( activecontents, anonymous, namedIsInternal )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia, hopefully )+import Darcs.Patch.Progress( progressFL )+import Darcs.Patch.Witnesses.Ordered+ ( FL(..), (:\/:)(..), (:/\:)(..), (+>+),+ mapFL_FL, concatFL, filterOutFLFL )+import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), seal )+ import Darcs.Repository.Flags ( UseIndex , ScanKnown@@ -41,32 +60,31 @@ , Compression (..) , WantGuiPause (..) , DiffAlgorithm (..)+ , UseCache(..)+ , LookForMoves(..)+ , LookForReplaces(..) )-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- ( FL(..), (:\/:)(..), (:/\:)(..), (+>+),- mapFL_FL, concatFL )-import Darcs.Patch.Witnesses.Sealed( Sealed(Sealed), seal )-import Darcs.Patch.Witnesses.Unsafe ( unsafeCoercePEnd )-import Darcs.Repository.InternalTypes( Repository(..) )-import Darcs.Repository.State( unrecordedChanges, readUnrecorded )+import Darcs.Repository.Hashed+ ( tentativelyAddPatches_+ , applyToTentativePristine+ , tentativelyRemovePatches_+ , UpdatePristine(..) )+import Darcs.Repository.Identify ( identifyRepository )+import Darcs.Repository.InternalTypes ( Repository )+import Darcs.Repository.Pending ( setTentativePending, readPending ) import Darcs.Repository.Resolution ( standardResolution, externalResolution )-import Darcs.Repository.Internal ( announceMergeConflicts,- checkUnrecordedConflicts, MakeChanges(..),- setTentativePending, tentativelyAddPatch_,- applyToTentativePristine,- tentativelyReplacePatches,- UpdatePristine(..) )+import Darcs.Repository.State ( unrecordedChanges, readUnrecorded )++import Darcs.Util.Prompt ( promptYorn )+import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Progress( debugMessage )+import Darcs.Util.Printer.Color (fancyPrinters)+import Darcs.Util.Printer ( text, ($$), redText, putDocLnWith, ($$) ) +data MakeChanges = MakeChanges | DontMakeChanges deriving ( Eq )+ tentativelyMergePatches_ :: forall rt p wR wU wT wY wX- . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => MakeChanges -> Repository rt p wR wU wT -> String -> AllowConflicts -> UpdateWorking@@ -76,16 +94,14 @@ -> 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- let us = mapFL_FL hopefully usi- them = mapFL_FL hopefully themi- ((pc :: FL (PatchInfoAnd rt p) wT wMerged) :/\: us_merged)- <- return $ merge2FL (progressFL "Merging us" usi)- (progressFL "Merging them" themi)- pend <- unrecordedChanges diffingOpts r Nothing+tentativelyMergePatches_ mc r cmd allowConflicts updateWorking externalMerge+ wantGuiPause compression verbosity reorder diffingOpts@(_, _, dflag) us them = do+ (them_merged :/\: us_merged)+ <- return $ merge2FL (progressFL "Merging us" us)+ (progressFL "Merging them" them)+ pend <- unrecordedChanges diffingOpts NoLookForMoves NoLookForReplaces r Nothing anonpend <- n2pia `fmap` anonymous (fromPrims pend)- pend' :/\: pw <- return $ merge (pc :\/: anonpend :>: NilFL)+ pend' :/\: pw <- return $ merge (them_merged :\/: anonpend :>: NilFL) let pwprim = concatFL $ progressFL "Examining patches for conflicts" $ mapFL_FL (activecontents . hopefully) pw Sealed standard_resolved_pw <- return $ standardResolution pwprim@@ -93,10 +109,11 @@ when (allowConflicts == YesAllowConflictsAndMark) $ mapM_ backupByCopying $ listTouchedFiles standard_resolved_pw debugMessage "Announcing conflicts..."- have_conflicts <- announceMergeConflicts cmd allowConflicts externalMerge standard_resolved_pw+ have_conflicts <-+ announceMergeConflicts cmd allowConflicts externalMerge standard_resolved_pw debugMessage "Checking for unrecorded conflicts..." have_unrecorded_conflicts <- checkUnrecordedConflicts updateWorking $- mapFL_FL hopefully pc+ mapFL_FL hopefully them_merged debugMessage "Reading working directory..." working <- readUnrecorded r Nothing debugMessage "Working out conflicts in actual working directory..."@@ -111,47 +128,31 @@ (effect us +>+ pend) (effect them) pwprim debugMessage "Applying patches to the local directories..." when (mc == MakeChanges) $ do- 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- -- information that wX=wT => wMerged=wY so we have to coerce.- -- TODO: it's not really clear why if this is an optimisation in- -- practice, as pc would be trivial to calculate in this case and- -- there aren't any obvious memory benefits.- doChanges NilFL = applyps r (unsafeCoercePEnd themi)- doChanges _ = applyps r pc- r' <- doChanges usi+ -- these two cases result in the same trees (that's the idea of+ -- merging), so we only operate on the set of patches and do the+ -- adaption of pristine and pending in the common code below+ r' <- case reorder of+ NoReorder -> do+ tentativelyAddPatches_ DontUpdatePristine r+ compression verbosity updateWorking them_merged+ Reorder -> do+ -- we do not actually remove any effect in the end, so+ -- it would be wrong to update the unrevert bundle or+ -- the working tree or pending+ r1 <- tentativelyRemovePatches_ DontUpdatePristineNorRevert r+ compression NoUpdateWorking+ (filterOutFLFL (namedIsInternal . hopefully) us)+ r2 <- tentativelyAddPatches_ DontUpdatePristine r1+ compression verbosity NoUpdateWorking them+ tentativelyAddPatches_ DontUpdatePristine r2+ compression verbosity NoUpdateWorking+ (filterOutFLFL (namedIsInternal . hopefully) us_merged)+ -- must use the original r, not the updated one here:+ applyToTentativePristine r verbosity them_merged setTentativePending r' updateWorking (effect pend' +>+ pw_resolution)- when (reorder == Reorder) $- -- TODO: we end up applying the merged remote patches on top of the unmerged- -- local patches, then commuting out the unmerged local patches and finally- -- adding the merged local patches.- -- It would better to just remove the unmerged local patche, then apply the- -- unmerged remote patches and then the merged local patches.- -- The handling of 'unrecorded' might complicate this slightly so this- -- refactoring may be better deferred until we have reliable witness tracking- -- for repositories.- tentativelyReplacePatches r' compression YesUpdateWorking verbosity us_merged return $ seal (effect pwprim +>+ pw_resolution)- where- 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 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- debugMessage "Applying patches to pristine..."- applyToTentativePristine repo verbosity ps- return repo' -tentativelyMergePatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+tentativelyMergePatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> String -> AllowConflicts -> UpdateWorking -> ExternalMerge -> WantGuiPause@@ -163,7 +164,7 @@ tentativelyMergePatches = tentativelyMergePatches_ MakeChanges -considerMergeToWorking :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+considerMergeToWorking :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> String -> AllowConflicts -> UpdateWorking -> ExternalMerge -> WantGuiPause@@ -173,4 +174,57 @@ -> FL (PatchInfoAnd rt p) wX wY -> IO (Sealed (FL (PrimOf p) wU)) considerMergeToWorking = tentativelyMergePatches_ DontMakeChanges+++announceMergeConflicts :: (PrimPatch p)+ => String+ -> AllowConflicts+ -> ExternalMerge+ -> FL p wX wY+ -> IO Bool+announceMergeConflicts cmd allowConflicts externalMerge resolved_pw =+ case nubSort $ listTouchedFiles resolved_pw of+ [] -> return False+ cfs -> if allowConflicts `elem` [YesAllowConflicts,YesAllowConflictsAndMark]+ || externalMerge /= NoExternalMerge+ then do putDocLnWith fancyPrinters $+ redText "We have conflicts in the following files:" $$ text (unlines cfs)+ return True+ else do putDocLnWith fancyPrinters $+ redText "There are conflicts in the following files:" $$ text (unlines cfs)+ fail $ "Refusing to "++cmd++" patches leading to conflicts.\n"+++ "If you would rather apply the patch and mark the conflicts,\n"+++ "use the --mark-conflicts or --allow-conflicts options to "++cmd++"\n"+++ "These can set as defaults by adding\n"+++ " "++cmd++" mark-conflicts\n"+++ "to "++darcsdir++"/prefs/defaults in the target repo. "++checkUnrecordedConflicts :: forall rt p wT wY. RepoPatch p+ => UpdateWorking+ -> 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 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+ pend ->+ case merge (fromPrims_ pend :\/: fromPrims_ (concatFL $ mapFL_FL effect pc)) of+ _ :/\: pend' ->+ case listConflictedFiles pend' of+ [] -> return False+ fs -> do putStrLn ("You have conflicting local changes to:\n"+ ++ unwords fs)+ confirmed <- promptYorn "Proceed?"+ unless confirmed $+ do putStrLn "Cancelled."+ exitSuccess+ return True+ fromPrims_ :: FL (PrimOf p) wA wB -> FL p wA wB+ fromPrims_ = fromPrims+
@@ -1,46 +0,0 @@--- Copyright (C) 2002-2004 David Roundy------ This program is free software; you can redistribute it and/or modify--- it under the terms of the GNU General Public License as published by--- the Free Software Foundation; either version 2, or (at your option)--- any later version.------ This program is distributed in the hope that it will be useful,--- but WITHOUT ANY WARRANTY; without even the implied warranty of--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the--- GNU General Public License for more details.------ You should have received a copy of the GNU General Public License--- along with this program; see the file COPYING. If not, write to--- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,--- Boston, MA 02110-1301, USA.--module Darcs.Repository.Motd- ( getMotd- , 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.Util.External ( fetchFilePS, Cachable(..) )-import Darcs.Util.Global ( darcsdir )-import Darcs.Util.Exception ( catchall )---- | Fetch and return the message of the day for a given repository.-getMotd :: String -> IO B.ByteString-getMotd repo = fetchFilePS motdPath (MaxAge 600) `catchall` return B.empty- where- motdPath = repo ++ "/" ++ darcsdir ++ "/prefs/motd"---- | Display the message of the day for a given repository,-showMotd :: String -> IO ()-showMotd repo = do- motd <- getMotd repo- unless (B.null motd) $ do- B.hPut stdout motd- putStrLn $ replicate 22 '*'
@@ -15,11 +15,8 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, ScopedTypeVariables #-}-- module Darcs.Repository.Old ( readOldRepo,- revertTentativeChanges, oldRepoFailMsg ) where+ oldRepoFailMsg ) where import Prelude () import Darcs.Prelude@@ -33,62 +30,61 @@ patchInfoAndPatch, actually, unavailable ) -import qualified Data.ByteString.Char8 as BC (break, pack)+import qualified Data.ByteString as B ( ByteString, null )+import qualified Data.ByteString.Char8 as BC ( break, pack, unpack ) import Darcs.Patch ( RepoPatch, IsRepoType, WrappedNamed, readPatch )-+import Darcs.Patch.ReadMonads as RM ( parseStrictly ) 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.Info ( PatchInfo(..), makePatchname, readPatchInfo, displayPatchInfo ) import Darcs.Patch.Set ( PatchSet(..), Tagged(..), SealedPatchSet, Origin ) import Darcs.Util.External ( gzFetchFilePS , Cachable(..)- , cloneFile )-import Darcs.Util.Lock ( writeBinFile )-import Darcs.Util.Printer ( renderString, RenderMode(..) )+import Darcs.Util.Printer ( renderString ) import Darcs.Util.Global ( darcsdir )+import Darcs.Util.Hash ( sha1PS )+import Darcs.Util.IsoDate ( readUTCDateOldFashioned, showIsoDateTime ) import Control.Exception ( catch, IOException ) -#include "impossible.h"- 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- beginTedious k- readRepoPrivate k realdir "inventory" `catch`+readOldRepo repo_dir = do+ realdir <- toPath `fmap` ioAbsoluteOrRemote repo_dir+ let task = "Reading inventory of repository "++repo_dir+ beginTedious task+ readRepoPrivate task realdir "inventory" `catch` (\e -> do hPutStrLn stderr ("Invalid repository: " ++ realdir) ioError e) 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- let parse inf = parse2 inf $ d </> darcsdir </> "patches" </> makeFilename inf- (mt, is) = case BC.break ('\n' ==) i of+readRepoPrivate task repo_dir inventory_name = do+ inventory <- gzFetchFilePS (repo_dir </> darcsdir </> inventory_name) Uncachable+ finishedOneIO task inventory_name+ let parse inf = parse2 inf $ repo_dir </> darcsdir </> "patches" </> makeFilename inf+ (mt, is) = case BC.break ('\n' ==) inventory of (swt,pistr) | swt == BC.pack "Starting with tag:" -> case readPatchInfos pistr of (t:ids) -> (Just t,reverse ids) [] -> bug "bad inventory in readRepoPrivate"- _ -> (Nothing, reverse $ readPatchInfos i)+ _ -> (Nothing, reverse $ readPatchInfos inventory) Sealed ts <- unseal seal `fmap` unsafeInterleaveIO (read_ts parse mt) Sealed ps <- unseal seal `fmap` unsafeInterleaveIO (read_patches parse is) return $ seal (PatchSet ts ps) where read_ts :: RepoPatch p => (forall wB . PatchInfo -> IO (Sealed (PatchInfoAnd rt p wB))) -> Maybe PatchInfo -> IO (Sealed (RL (Tagged rt p) Origin))- read_ts _ Nothing = do endTedious k+ read_ts _ Nothing = do endTedious task return $ seal NilRL read_ts parse (Just tag0) =- do debugMessage $ "Looking for inventory for:\n"++ renderString Encode (showPatchInfo tag0)+ do debugMessage $ "Looking for inventory for:\n"++ renderString (displayPatchInfo tag0) i <- unsafeInterleaveIO $- do x <- gzFetchFilePS (d </> darcsdir </> "inventories" </> makeFilename tag0) Uncachable- finishedOneIO k (renderString Encode (showPatchInfo tag0))+ do x <- gzFetchFilePS (repo_dir </> darcsdir </> "inventories" </> makeFilename tag0) Uncachable+ finishedOneIO task (renderString (displayPatchInfo tag0)) return x let (mt, is) = case BC.break ('\n' ==) i of (swt,pistr) | swt == BC.pack "Starting with tag:" ->@@ -128,12 +124,30 @@ Sealed y <- unseal seal `fmap` unsafeInterleaveIO ioy return $ seal $ f y x --revertTentativeChanges :: IO ()-revertTentativeChanges =- do cloneFile (darcsdir++"/inventory") (darcsdir++"/tentative_inventory")- writeBinFile (darcsdir++"/tentative_pristine") ""- oldRepoFailMsg :: String oldRepoFailMsg = "ERROR: repository upgrade required, try `darcs optimize upgrade`\n" ++ "See http://wiki.darcs.net/OF for more details."++-- | This makes darcs-1 (non-hashed repos) filenames.+--+-- The name consists of three segments:+--+-- * timestamp (ISO8601-compatible yyyymmmddHHMMSS;+-- note that the old-fashioned (non-hashed) format expects this date to+-- be exactly as in the patch, /ignoring/ any timezone info,+-- which is why we use 'readUTCDateOldFashioned' here)+--+-- * SHA1 hash of the author+--+-- * SHA1 hash of the patch name, author, date, log, and \"inverted\"+-- flag.+makeFilename :: PatchInfo -> String+makeFilename pi = showIsoDateTime d++"-"++sha1_a++"-"++ (show $ makePatchname pi) ++ ".gz"+ where d = readUTCDateOldFashioned $ BC.unpack $ _piDate pi+ sha1_a = take 5 $ show $ sha1PS $ _piAuthor pi++readPatchInfos :: B.ByteString -> [PatchInfo]+readPatchInfos inv | B.null inv = []+readPatchInfos inv = case parseStrictly readPatchInfo inv of+ Just (pinfo,r) -> pinfo : readPatchInfos r+ _ -> []
@@ -1,26 +1,50 @@+{-|+License : GPL-2++Packs are an optimization that enable faster repository cloning over HTTP.+A pack is actually a @tar.gz@ file that contains many files that would otherwise+have to be transfered one by one (which is much slower over HTTP).++Two packs are created at the same time by 'createPacks':++ 1. The basic pack, contains the latest recorded version of the working tree.+ 2. The patches pack, contains the set of patches of the repository.++The paths of these files are @_darcs\/packs\/basic.tar.gz@ and+@_darcs\/packs\/patches.tar.gz@. There is also @_darcs\/packs\/pristine@ which+indicates the pristine hash at the moment of the creation of the packs. This+last file is useful to determine whether the basic pack is in sync with the+current pristine of the repository.+-}+ module Darcs.Repository.Packs ( fetchAndUnpackBasic , fetchAndUnpackPatches , packsDir+ , createPacks ) where import qualified Codec.Archive.Tar as Tar+import Codec.Archive.Tar.Entry ( fileEntry, toTarPath ) 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 Control.Exception ( Exception, IOException, throwIO, catch, finally )+import Control.Monad ( void, when, unless ) import System.IO.Error ( isAlreadyExistsError )+import System.IO.Unsafe ( unsafeInterleaveIO ) -import qualified Data.ByteString.Lazy.Char8 as BL-import Data.List ( isPrefixOf )+import qualified Data.ByteString.Lazy.Char8 as BLC+import Data.List ( isPrefixOf, sort ) import Data.Maybe( catMaybes, listToMaybe ) import System.Directory ( createDirectoryIfMissing , renameFile+ , removeFile , doesFileExist+ , getModificationTime ) import System.FilePath ( (</>)+ , (<.>) , takeFileName , splitPath , joinPath@@ -28,8 +52,22 @@ ) import System.Posix.Files ( createLink ) +import Darcs.Util.ByteString ( gzReadFilePS ) import Darcs.Util.Lock ( withTemp ) import Darcs.Util.External ( Cachable(..), fetchFileLazyPS )+import Darcs.Util.Global ( darcsdir )+import Darcs.Util.Progress ( debugMessage )++import Darcs.Patch ( IsRepoType, RepoPatch )+import Darcs.Patch.PatchInfoAnd ( extractHash )+import Darcs.Patch.Witnesses.Ordered ( mapFL )+import Darcs.Patch.Set ( patchSet2FL )++import Darcs.Repository.InternalTypes ( Repository )+import qualified Darcs.Repository.Hashed as HashedRepo+import Darcs.Repository.Hashed ( filterDirContents, readRepo, readHashedPristineRoot )+import Darcs.Repository.Format+ ( identifyRepoFormat, formatHas, RepoProperty ( HashedInventory ) ) import Darcs.Repository.Cache ( fetchFileUsingCache , HashedDir(..) , Cache(..)@@ -39,12 +77,12 @@ , bucketFolder , CacheType(Directory) )--import Darcs.Util.Global ( darcsdir )-import Darcs.Util.Progress ( debugMessage )+import Darcs.Repository.Old ( oldRepoFailMsg ) -packsDir :: String-packsDir = "packs"+packsDir, basicPack, patchesPack :: String+packsDir = "packs"+basicPack = "basic.tar.gz"+patchesPack = "patches.tar.gz" fetchAndUnpack :: FilePath -> HashedDir@@ -60,11 +98,11 @@ -- 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+ withAsync (fetchAndUnpack patchesPack HashedInventoriesDir cache remote) $ \_ -> do fetchFilesUsingCache cache HashedPatchesDir paths fetchAndUnpackBasic :: Cache -> FilePath -> IO ()-fetchAndUnpackBasic = fetchAndUnpack "basic.tar.gz" HashedPristineDir+fetchAndUnpackBasic = fetchAndUnpack basicPack HashedPristineDir unpackTar :: Exception e => Cache -> HashedDir -> Tar.Entries e -> IO () unpackTar _ _ Tar.Done = return ()@@ -87,7 +125,7 @@ _ -> fail "Unexpected non-file tar entry" where writeFile' Nothing path content = withTemp $ \tmp -> do- BL.writeFile tmp content+ BLC.writeFile tmp content renameFile tmp path writeFile' (Just ca) path content = do let fileFullPath = case splitPath path of@@ -115,3 +153,58 @@ cacheDir (Ca cs) = listToMaybe . catMaybes .flip map cs $ \x -> case x of Cache Directory Writable x' -> Just x' _ -> Nothing++-- | Create packs from the current recorded version of the repository.+createPacks :: (IsRepoType rt, RepoPatch p)+ => Repository rt p wR wU wT -> IO ()+createPacks repo = flip finally (mapM_ removeFileIfExists+ [ darcsdir </> "meta-filelist-inventories"+ , darcsdir </> "meta-filelist-pristine"+ , basicTar <.> "part"+ , 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 (darcsdir </> packsDir)+ -- pristine hash+ Just hash <- readHashedPristineRoot repo+ writeFile ( darcsdir </> packsDir </> "pristine" ) hash+ -- pack patchesTar+ ps <- mapFL hashedPatchFileName . patchSet2FL <$> readRepo repo+ is <- map ((darcsdir </> "inventories") </>) <$> HashedRepo.listInventories+ writeFile (darcsdir </> "meta-filelist-inventories") . unlines $+ map takeFileName is+ -- Note: tinkering with zlib's compression parameters does not make+ -- any noticeable difference in generated archive size;+ -- switching to bzip2 would provide ~25% gain OTOH.+ BLC.writeFile (patchesTar <.> "part") . GZ.compress . Tar.write =<<+ mapM fileEntry' ((darcsdir </> "meta-filelist-inventories") : ps +++ reverse is)+ renameFile (patchesTar <.> "part") patchesTar+ -- pack basicTar+ pr <- sortByMTime =<< dirContents "pristine.hashed"+ writeFile (darcsdir </> "meta-filelist-pristine") . unlines $+ map takeFileName pr+ BLC.writeFile (basicTar <.> "part") . GZ.compress . Tar.write =<< mapM fileEntry' (+ [ darcsdir </> "meta-filelist-pristine"+ , darcsdir </> "hashed_inventory"+ ] ++ reverse pr)+ renameFile (basicTar <.> "part") basicTar+ where+ basicTar = darcsdir </> packsDir </> basicPack+ patchesTar = darcsdir </> packsDir </> patchesPack+ fileEntry' x = unsafeInterleaveIO $ do+ content <- BLC.fromChunks . return <$> gzReadFilePS x+ tp <- either fail return $ toTarPath False x+ return $ fileEntry tp content+ dirContents d = map ((darcsdir </> d) </>) <$>+ filterDirContents d (const True)+ hashedPatchFileName x = case extractHash x of+ Left _ -> fail "unexpected unhashed patch"+ Right h -> darcsdir </> "patches" </> h+ sortByMTime xs = map snd . sort <$> mapM (\x -> (\t -> (t, x)) <$>+ getModificationTime x) xs+ removeFileIfExists x = do+ ex <- doesFileExist x+ when ex $ removeFile x
@@ -1,41 +1,42 @@-{-# LANGUAGE CPP, NamedFieldPuns #-}+{-# LANGUAGE NamedFieldPuns #-} --- Copyright (C) 2009-2010 Benedikt Schmidt------ 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.+{-|+License : GPL-2 +The patch-index stores additional information that is extracted from+the PatchSet for the repository to speed up certain commands (namely+@log@ and @annotate@). More precisely, for every file tracked by the+repository, it stores the list of patches that touch it.++When created, patch-index lives in @_darcs\/patch_index\/@, and it+should be automatically maintained each time the set of patches of+the repository is updated.++Patch-index can also be explicitely disabled by creating a file+@_darcs\/no_patch_index@. "Explicitely disabed" means that no command+should attempt to automatically create the patch-index.++See <http://darcs.net/Internals/PatchIndex> for more information.+-} module Darcs.Repository.PatchIndex ( doesPatchIndexExist, isPatchIndexDisabled, isPatchIndexInSync, canUsePatchIndex,- canCreatePI, createPIWithInterrupt, createOrUpdatePatchIndexDisk, deletePatchIndex,- dumpPatchIndex,- filterPatches,+ attemptCreatePatchIndex, PatchFilter, maybeFilterPatches, getRelevantSubsequence,- piTest,- attemptCreatePatchIndex+ dumpPatchIndex,+ piTest ) where -import Prelude hiding ( pi, (<$>) )+import Prelude ()+import Darcs.Prelude+ import Data.Binary ( encodeFile, decodeFile ) import Data.Word ( Word32 ) import Data.Int ( Int8 )@@ -51,44 +52,28 @@ import Control.Applicative ( (<$>) ) import System.Directory ( createDirectory, renameDirectory, doesFileExist, doesDirectoryExist ) import Darcs.Repository.Format ( formatHas, RepoProperty( HashedInventory ) )-import Darcs.Repository.InternalTypes ( Repository(..) )-import Darcs.Repository.HashedRepo ( readRepo )+import Darcs.Repository.InternalTypes ( Repository, repoLocation, repoFormat ) 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.Util.Lock ( withPermDir, rmRecursive )-import Darcs.Patch ( RepoPatch, IsRepoType, listTouchedFiles )+import Darcs.Patch ( RepoPatch, listTouchedFiles ) import Darcs.Util.Path ( FileName, fp2fn, fn2fp, toFilePath )-import Darcs.Patch.Apply ( applyToFileMods, ApplyState(..) )-import Darcs.Patch.Set ( newset2FL, Origin, newset2FL )-import Darcs.Patch.Patchy ( Commute )+import Darcs.Patch.Apply ( ApplyState(..) )+import Darcs.Patch.Set ( PatchSet(..), patchSet2FL, Origin, patchSet2FL ) import Darcs.Patch.Inspect ( PatchInspect ) import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Progress ( debugMessage ) import Darcs.Patch.Index.Types+import Darcs.Patch.Index.Monad ( applyToFileMods, makePatchID ) import System.FilePath( (</>) ) import System.IO (openFile, IOMode(WriteMode), hClose) import qualified Data.ByteString as B-import Darcs.Util.Crypt.SHA256 (sha256sum )-import Darcs.Util.Crypt.SHA1 ( SHA1(..), showAsHex )+import Darcs.Util.Hash ( sha256sum, showAsHex ) import Darcs.Util.Tree ( Tree(..) ) import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) import Darcs.Util.SignalHandler ( catchInterrupt )-#include "impossible.h" -{- ------------------------------------------------------------------------------ The patch index stores additional information that is extracted from- the PatchSet for the repository to speed up certain commands.-- createPatchIndexDisk:- Create the on-disk patch-index index from scratch.- updatePatchIndexDisk:- Update the on-disk patch-index index.- ----------------------------------------------------------------------------- -}---- ------------------------------------------------------------------------ Data structures for the patch-index- data FileIdSpan = FidSpan !FileId -- the fileid has some fixed name in the !PatchId -- span starting here@@ -128,26 +113,13 @@ infom::InfoMap } --- | an empty patch-index-emptyPatchIndex :: PatchIndex-emptyPatchIndex = PatchIndex [] M.empty M.empty M.empty- -- | On-disk version of patch index -- version 1 is the one introduced in darcs 2.10 -- 2 changes the pids order to newer-to-older version :: Int8 version = 2 --- ------------------------------------------------------------------------ Query the patch-index--getInventoryHash :: FilePath -> IO String-getInventoryHash repodir = do- inv <- B.readFile (repodir </> darcsdir </> "hashed_inventory")- return $ sha256sum inv---- ------------------------------------------------------------------------ create patch-index+type PIM a = State PatchIndex a -- | 'applyPatchMods pmods pindex' applies a list of PatchMods to the given -- patch index pindex@@ -155,12 +127,12 @@ applyPatchMods pmods pindex = flip execState pindex $ mapM_ goList pmods where goList :: (PatchId, [PatchMod FileName]) -> PIM ()- -- nubSeq handles invalid patch in darcs repo:- -- move with identical target name "rename darcs_patcher to darcs-patcher." goList (pid, mods) = do modify (\pind -> pind{pids = pid:pids pind}) mapM_ (curry go pid) (nubSeq mods)-+ -- nubSeq handles invalid patch in darcs repo:+ -- move with identical target name "rename darcs_patcher to darcs-patcher."+ nubSeq = map head . group go :: (PatchId, PatchMod FileName) -> PIM () go (pid, PCreateFile fn) = do fid <- createFidStartSpan fn pid@@ -196,11 +168,6 @@ Just [] -> error $ "applyPatchMods: impossible, no entry for "++show fn ++" in FileIdSpans in duplicate, empty list" --- ------------------------------------------------------------------------ Update and query patch index--type PIM a = State PatchIndex a- -- | create new filespan for created file createFidStartSpan :: FileName -> PatchId -> PIM FileId createFidStartSpan fn pstart = do@@ -309,24 +276,18 @@ Nothing -> error "lookupFids' : could not find file" else return [] --- | remove sequential duplicates-nubSeq :: Eq a => [a] -> [a]-nubSeq = map head . group---- ------------------------------------------------------------------------ Create/Update patch-index on disk---- | create patch index that corresponds to all patches in repo+-- | Creates patch index that corresponds to all patches in repo. 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+ :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> PatchSet rt p Origin wR+ -> IO ()+createPatchIndexDisk repository ps = do+ let patches = mapFL Sealed2 $ patchSet2FL ps createPatchIndexFrom repository $ patches2patchMods patches S.empty -- | convert patches to patchmods-patches2patchMods :: (Apply p, Commute p, PatchInspect p, ApplyState p ~ Tree)+patches2patchMods :: (Apply p, PatchInspect p, ApplyState p ~ Tree) => [Sealed2 (PatchInfoAnd rt p)] -> Set FileName -> [(PatchId, [PatchMod FileName])] patches2patchMods patches fns = snd $ mapAccumL go fns patches where@@ -387,20 +348,22 @@ -- | update the patch index to the current state of the repository updatePatchIndexDisk- :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT -> IO ()-updatePatchIndexDisk repo@(Repo repodir _ _ _) = do+ :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> PatchSet rt p Origin wR+ -> IO ()+updatePatchIndexDisk repo patches = do+ let repodir = repoLocation repo (_,_,pid2idx,pindex) <- loadPatchIndex repodir -- check that patch index is up to date- patches <- newset2FL `fmap` readRepo repo repodir- let pidsrepo = mapFL (makePatchID . info) patches+ let flpatches = patchSet2FL patches+ let pidsrepo = mapFL (makePatchID . info) flpatches (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 repodir- let newpatches = drop len_common $ mapFL seal2 rawpatches+ let newpatches = drop len_common $ mapFL seal2 flpatches newpmods = patches2patchMods newpatches filenames inv_hash <- getInventoryHash repodir storePatchIndex repodir cdir inv_hash (applyPatchMods newpmods pindex')@@ -414,18 +377,21 @@ -- | 'createPatchIndexFrom repo pmods' creates a patch index from the given -- patchmods.-createPatchIndexFrom :: RepoPatch p => Repository rt p wR wU wT+createPatchIndexFrom :: Repository rt p wR wU wT -> [(PatchId, [PatchMod FileName])] -> IO ()-createPatchIndexFrom (Repo repodir _ _ _) pmods = do+createPatchIndexFrom repo pmods = do inv_hash <- getInventoryHash repodir storePatchIndex repodir cdir inv_hash (applyPatchMods pmods emptyPatchIndex)- where cdir = repodir </> indexDir-+ where repodir = repoLocation repo+ cdir = repodir </> indexDir+ emptyPatchIndex = PatchIndex [] M.empty M.empty M.empty --- ------------------------------------------------------------------------ Load/Store patch-Index+getInventoryHash :: FilePath -> IO String+getInventoryHash repodir = do+ inv <- B.readFile (repodir </> darcsdir </> "hashed_inventory")+ return $ sha256sum inv --- | load patch-index from disk+-- | Load patch-index from disk along with some meta data. loadPatchIndex :: FilePath -> IO (Int8, String, Map PatchId Int, PatchIndex) loadPatchIndex repodir = do let pindex_dir = repodir </> indexDir@@ -437,65 +403,66 @@ fpspans <- loadFpMap (pindex_dir </> fpMapFile) return (v, inv_hash, pid2idx, PatchIndex pids fidspans fpspans infom) --- | load patch-index,--- | ensuring that whenever loaded, the patch-index--- | can actually be read by the current version of darcs,--- | and up to date.-loadSafePatchIndex :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+-- | If patch-index is useful as it is now, read it. If not, create or update it, then read it.+loadSafePatchIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT- -> IO (Map PatchId Int, PatchIndex)-loadSafePatchIndex repo@(Repo repodir _ _ _) = do+ -> PatchSet rt p Origin wR -- ^ PatchSet of the repository, used if we need to create the patch-index.+ -> IO PatchIndex+loadSafePatchIndex repo ps = do+ let repodir = repoLocation repo can_use <- isPatchIndexInSync repo- (_,_,pid2idx,pi) <-+ (_,_,_,pi) <- if can_use then loadPatchIndex repodir- else do createOrUpdatePatchIndexDisk repo+ else do createOrUpdatePatchIndexDisk repo ps loadPatchIndex repodir- return (pid2idx, pi)+ return pi --- | check if patch-index exits for this repository+-- | Read-only. Checks if patch-index exists for this repository+-- it works by checking if:+--+-- 1. @_darcs\/patch_index\/@ and its corresponding files are all present+-- 2. patch index version is the one handled by this version of Darcs doesPatchIndexExist :: FilePath -> IO Bool doesPatchIndexExist repodir = do- filesArePresent <- fmap and $ mapM (doesFileExist . (pindex_dir </>))+ filesArePresent <- and <$> mapM (doesFileExist . (pindex_dir </>)) [repoStateFile, pidsFile, touchMapFile, fidMapFile, fpMapFile] if filesArePresent- then do (v, _, _, _) <- loadPatchIndex repodir+ then do v <- piVersion return (v == version) -- consider PI only of on-disk format is the current one else return False where pindex_dir = repodir </> indexDir+ piVersion = fst <$> loadRepoState (pindex_dir </> repoStateFile) --- | check if noPatchIndex exists+-- | Read-only. Checks if @_darcs\/noPatchIndex@ exists, that is, if patch-index is explicitely disabled. isPatchIndexDisabled :: FilePath -> IO Bool isPatchIndexDisabled repodir = doesFileExist (repodir </> darcsdir </> noPatchIndex) --- | create or update patch index-createOrUpdatePatchIndexDisk :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT -> IO ()-createOrUpdatePatchIndexDisk repo@(Repo repodir _ _ _)= do+-- | Create or update patch index+--+-- 1. if @_darcs\/no_patch_index@ exists, delete it+-- 2. if patch index exists, update it+-- 3. if not, create it from scratch+createOrUpdatePatchIndexDisk :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> PatchSet rt p Origin wR -> IO ()+createOrUpdatePatchIndexDisk repo ps = do+ let repodir = repoLocation repo rmRecursive (repodir </> darcsdir </> noPatchIndex) `catch` \(_ :: IOError) -> return () dpie <- doesPatchIndexExist repodir if dpie- then updatePatchIndexDisk repo- else createPatchIndexDisk repo---- | 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 rt p wR wU wT- -> IO Bool-canCreatePI (Repo repodir format _ _) =- (not . or) <$> sequence [ doesntHaveHashedInventory format- , isPatchIndexDisabled repodir- , doesPatchIndexExist repodir- ]- where- doesntHaveHashedInventory = return . not . formatHas HashedInventory+ then updatePatchIndexDisk repo ps+ else createPatchIndexDisk repo ps --- | 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 rt p wR wU wT -> IO Bool-canUsePatchIndex (Repo repodir _ _ _) = do+-- | Read-only. Checks the two following things:+--+-- 1. 'doesPatchIndexExist'+-- 2. 'isPatchIndexDisabled'+--+-- Then only if it exists and it is not explicitely disabled, returns @True@, else returns @False@+-- (or an error if it exists and is explicitely disabled at the same time).+canUsePatchIndex :: Repository rt p wR wU wT -> IO Bool+canUsePatchIndex repo = do+ let repodir = repoLocation repo piExists <- doesPatchIndexExist repodir piDisabled <- isPatchIndexDisabled repodir case (piExists, piDisabled) of@@ -504,18 +471,22 @@ (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 :: (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- createPatchIndexDisk repo- putStrLn "Created patch index.") `catchInterrupt` (putStrLn "Patch Index Disabled" >> deletePatchIndex repodir)+-- | Creates patch-index (ignoring whether it is explicitely disabled).+-- If it is ctrl-c'ed, then aborts, delete patch-index and mark it as disabled.+createPIWithInterrupt :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> PatchSet rt p Origin wR -> IO ()+createPIWithInterrupt repo ps = do+ let repodir = repoLocation repo+ putStrLn "Creating a patch index, please wait. To stop press Ctrl-C"+ (do+ createPatchIndexDisk repo ps+ putStrLn "Created patch index.") `catchInterrupt` (putStrLn "Patch Index Disabled" >> deletePatchIndex repodir) --- | check if patch-index is in sync with repository-isPatchIndexInSync :: (RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT -> IO Bool-isPatchIndexInSync (Repo repodir _ _ _) = do+-- | Checks if patch-index exists and is in sync with repository (more precisely with @_darcs\/hashed_inventory@).+-- That is, checks if patch-index can be used as it is now.+isPatchIndexInSync :: Repository rt p wR wU wT -> IO Bool+isPatchIndexInSync repo = do+ let repodir = repoLocation repo dpie <- doesPatchIndexExist repodir if dpie then do@@ -524,7 +495,7 @@ return (inv_hash == inv_hash_pindex) else return False --- | store patch-index on disk+-- | Stores patch-index on disk. storePatchIndex :: FilePath -> FilePath -> String -> PatchIndex -> IO () storePatchIndex repodir cdir inv_hash (PatchIndex pids fidspans fpspans infom) = do createDirectory cdir `catch` \(_ :: IOError) -> return ()@@ -556,7 +527,7 @@ storeFidMap :: FilePath -> FileIdSpans -> IO () storeFidMap fp fidm = encodeFile fp $ M.map (map (\(FidSpan a b c) -> (a, b, toIdxM c))) fidm- where toIdxM (Nothing) = zero+ where toIdxM Nothing = zero toIdxM (Just pid) = pid loadFidMap :: FilePath -> IO FileIdSpans@@ -567,7 +538,7 @@ storeFpMap :: FilePath -> FilePathSpans -> IO () storeFpMap fp fidm = encodeFile fp $ M.map (map (\(FpSpan a b c) -> (a, b, toIdxM c))) fidm- where toIdxM (Nothing) = zero+ where toIdxM Nothing = zero toIdxM (Just pid) = pid loadFpMap :: FilePath -> IO FilePathSpans@@ -575,9 +546,6 @@ where toPidM pid | pid == zero = Nothing | otherwise = Just pid -zero :: PatchId-zero = PID $ SHA1 0 0 0 0 0- storeInfoMap :: FilePath -> InfoMap -> IO () storeInfoMap fp infom = encodeFile fp $ M.map (\fi -> (isFile fi, touching fi)) infom@@ -585,30 +553,17 @@ loadInfoMap :: FilePath -> IO InfoMap loadInfoMap fp = M.map (\(isF,pids) -> FileInfo isF pids) <$> decodeFile fp --- | Base directory for the patch index-indexDir :: String+indexDir, repoStateFile, pidsFile, fidMapFile, fpMapFile,+ touchMapFile, noPatchIndex :: String indexDir = darcsdir </> "patch_index"--repoStateFile :: String repoStateFile = "repo_state"--pidsFile :: String pidsFile = "patch_ids"--fidMapFile :: String fidMapFile = "fid_map"--fpMapFile :: String fpMapFile = "fp_map"--touchMapFile :: String touchMapFile = "touch_map"--noPatchIndex :: String noPatchIndex = "no_patch_index" --------------------------------------------------------------------------- Delete patch index+-- | Deletes patch-index (@_darcs\/patch_index\/@ and its contents) and mark repository as disabled (creates @_darcs\/no_patch_index@). deletePatchIndex :: FilePath -> IO () deletePatchIndex repodir = do exists <- doesDirectoryExist indexDir@@ -618,9 +573,6 @@ (openFile (repodir </> darcsdir </> noPatchIndex) WriteMode >>= hClose) `catch` \(e :: IOError) -> error $ "Error: Could not disable patch index\n" ++ show e --------------------------------------------------------------------------- Dump information in patch index- dumpRepoState :: [PatchId] -> String dumpRepoState = unlines . map pid2string @@ -648,34 +600,38 @@ fpSpans2filePaths' :: FileIdSpans -> [FilePath] fpSpans2filePaths' fidSpans = [fn2fp fp | (fp, _) <- M.toList fidSpans] -dumpPatchIndex :: FilePath -> IO ()-dumpPatchIndex repodir = do- (_,inv_hash,_,PatchIndex pids fidspans fpspans infom) <- loadPatchIndex repodir- putStrLn $ "Inventory hash:" ++ inv_hash- putStrLn "================="- putStrLn "Repo state:"- putStrLn "==========="- putStrLn $ dumpRepoState pids- putStrLn "Fileid spans:"- putStrLn "============="- putStrLn $ dumpFileIdSpans fidspans- putStrLn "Filepath spans:"- putStrLn "=============="- putStrLn $ dumpFilePathSpans fpspans- putStrLn "Info Map:"- putStrLn "========="- putStrLn $ dumpTouchingMap infom- putStrLn "Files:"- putStrLn "=============="- putStrLn $ unlines $ fpSpans2filePaths fpspans infom+-- | Checks if patch index can be created and build it with interrupt.+attemptCreatePatchIndex+ :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -> PatchSet rt p Origin wR -> IO ()+attemptCreatePatchIndex repo ps = do+ canCreate <- canCreatePI repo+ when canCreate $ createPIWithInterrupt repo ps --------------------------------------------------------------------------- Filtering functions based on FilePaths--- returns an RL in which the order of patches matters, for annotate to use-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+-- | 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 :: Repository rt p wR wU wT -> IO Bool+canCreatePI repo =+ (not . or) <$> sequence [ doesntHaveHashedInventory (repoFormat repo)+ , isPatchIndexDisabled repodir+ , doesPatchIndexExist repodir+ ]+ where+ repodir = repoLocation repo+ doesntHaveHashedInventory = return . not . formatHas HashedInventory++-- | Returns an RL in which the order of patches matters. Useful for the @annotate@ command.+-- If patch-index does not exist and is not explicitely disabled, silently create it.+-- (Also, if it is out-of-sync, which should not happen, silently update it).+getRelevantSubsequence :: (RepoPatch p, ApplyState p ~ Tree, a ~ PatchInfoAnd rt p)+ => Sealed ((RL a) wK) -- ^ Sequence of patches you want to filter+ -> Repository rt p wR wU wR -- ^ The repository (to attempt loading patch-index from its path)+ -> PatchSet rt p Origin wR -- ^ PatchSet of repository (in case we need to create patch-index)+ -> [FileName] -- ^ File(s) about which you want patches from given sequence+ -> IO (Sealed ((RL a) Origin)) -- ^ Filtered sequence of patches.+getRelevantSubsequence pxes repository ps fns = do+ pi@(PatchIndex _ _ _ infom) <- loadSafePatchIndex repository ps let fids = map (\fn -> evalState (lookupFid fn) pi) fns pidss = map ((\(FileInfo _ a) -> a).fromJust.(`M.lookup` infom)) fids pids = S.unions pidss@@ -686,38 +642,58 @@ => 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 (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 :: (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+ | short (makePatchID $ info x) `S.member` pids = keepElems xs (acc:<:x) pids+ | otherwise = keepElems (unsafeCoerceP xs) acc pids 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.+-- | If a patch index is available, returns a filter that takes a list of files and returns+-- a @PatchFilter@ that only keeps patches that modify the given list of files.+-- If patch-index cannot be used, return the original input.+-- If patch-index does not exist and is not explicitely disabled, silently create it.+-- (Also, if it is out-of-sync, which should not happen, silently update it). maybeFilterPatches- :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT- -> PatchFilter rt p-maybeFilterPatches repo fps ops = do+ :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT -- ^ The repository+ -> PatchSet rt p Origin wR -- ^ PatchSet of patches of repository (in case patch-index needs to be created)+ -> PatchFilter rt p -- ^ PatchFilter ready to be used by SelectChanges.+maybeFilterPatches repo ps fps ops = do usePI <- canUsePatchIndex repo- -- in theory we could change the type signature to make this function staged,- -- but it doesn't seem worth it.- if usePI then filterPatches repo fps ops else return ops-+ if usePI+ then do+ pi@(PatchIndex _ _ _ infom) <- loadSafePatchIndex repo ps+ 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+ else return ops --------------------------------------------------------------------------- Test patch index+-- | Dump information in patch index. Patch-index should be checked to exist beforehand. Read-only.+dumpPatchIndex :: FilePath -> IO ()+dumpPatchIndex repodir = do+ (_,inv_hash,_,PatchIndex pids fidspans fpspans infom) <- loadPatchIndex repodir+ putStrLn $ unlines $+ [ "Inventory hash:" ++ inv_hash+ , "================="+ , "Repo state:"+ , "==========="+ , dumpRepoState pids+ , "Fileid spans:"+ , "============="+ , dumpFileIdSpans fidspans+ , "Filepath spans:"+ , "=============="+ , dumpFilePathSpans fpspans+ , "Info Map:"+ , "========="+ , dumpTouchingMap infom+ , "Files:"+ , "=============="+ ] ++ fpSpans2filePaths fpspans infom -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+-- | Read-only sanity check on patch-index. Patch-index should be checked to exist beforehand. It may not be in sync with repository.+piTest :: FilePath -> IO ()+piTest repodir = do+ (_,_,_,PatchIndex rpids fidspans fpspans infom) <- loadPatchIndex repodir let pids = reverse rpids -- test fidspans@@ -760,9 +736,4 @@ isInOrder [] _ = True isInOrder _ [] = False --- | Check if patch index can be created and build it with interrupt.-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+
@@ -16,15 +16,16 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.Repository.Pending ( readPending- , readTentativePending- , writeTentativePending+ , siftForPending+ , tentativelyRemoveFromPending+ , finalizePending+ , makeNewPending+ , tentativelyAddToPending+ , setTentativePending+ , prepend -- deprecated interface:- , readNewPending- , writeNewPending , pendingName ) where @@ -32,21 +33,57 @@ import Darcs.Prelude import Control.Applicative-import qualified Data.ByteString as BS ( empty )+import qualified Data.ByteString as B ( empty ) +import Control.Exception ( catch, IOException )+import Data.Maybe ( fromMaybe )++import Darcs.Util.Printer ( errorDoc ) import Darcs.Util.Global ( darcsdir )-import Darcs.Util.Lock ( writeDocBinFile )-import Darcs.Repository.InternalTypes ( Repository(..) )-import Darcs.Patch ( readPatch, RepoPatch, PrimOf )+import Darcs.Util.Lock+ ( writeDocBinFile+ , removeFileMayNotExist+ )+import Darcs.Repository.InternalTypes ( Repository, withRepoLocation )+import Darcs.Repository.Flags+ ( UpdateWorking (..))+import Darcs.Patch+ ( readPatch, RepoPatch, PrimOf, tryToShrink+ , primIsHunk, primIsBinary, commute, invert+ , primIsAddfile, primIsAdddir, commuteFLorComplain+ , effect, primIsSetpref, applyToTree )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd )+import Darcs.Patch.Progress (progressFL)+import Darcs.Patch.Permutations ( commuteWhatWeCanFL+ , removeFL+ )++import Darcs.Patch.Prim ( tryShrinkingInverse+ , PrimPatch+ ) 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.Patch.Show ( ShowPatchBasic(..), ShowPatchFor(ForStorage) )+import Darcs.Patch.Apply ( ApplyState ) +import Darcs.Util.Tree ( Tree )+import Darcs.Util.Exception ( catchall )+import Darcs.Util.Workaround ( renameFile )+import Darcs.Patch.Witnesses.Eq ( EqCheck(..) )+import Darcs.Patch.Witnesses.Sealed+ ( Sealed(Sealed), mapSeal, seal+ , FlippedSeal(FlippedSeal)+ , flipSeal+ )+import Darcs.Patch.Witnesses.Unsafe+ ( unsafeCoerceP, unsafeCoercePStart )+import Darcs.Patch.Witnesses.Ordered+ ( FL(..), RL(..), (:>)(..), (+>+)+ , lengthFL, allFL, filterOutFLFL+ , reverseFL, mapFL ) import Darcs.Util.ByteString ( gzReadFilePS )-import Darcs.Util.Printer ( Doc, ($$), (<>), text, vcat )+import Darcs.Util.Printer ( Doc, ($$), (<>), text, vcat, (<+>) )+import Darcs.Util.Progress ( debugMessage ) pendingName :: String pendingName = darcsdir ++ "/patches/pending"@@ -77,21 +114,22 @@ 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+ pend <- gzReadFilePS (pendingName ++ suffix) `catchall` return B.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;+-- 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.+-- (*) bf: This function does not exist. 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+ showPatch f = showMaybeBracketedFL (showPatch f) '{' '}' . unFLM readMaybeBracketedFL :: forall m p wX . ParserM m => (forall wY . m (Sealed (p wY))) -> Char -> Char@@ -126,4 +164,228 @@ name = pendingName ++ suffix writePatch :: ShowPatchBasic p => FilePath -> p wX wY -> IO ()-writePatch f p = writeDocBinFile f $ showPatch p <> text "\n"+writePatch f p = writeDocBinFile f $ showPatch ForStorage p <> text "\n"++-- | @siftForPending ps@ simplifies the candidate pending patch @ps@+-- through a combination of looking for self-cancellations+-- (sequences of patches followed by their inverses), coalescing,+-- and getting rid of any hunk/binary patches we can commute out+-- the back+--+-- The visual image of sifting can be quite helpful here. We are+-- repeatedly tapping (shrinking) the patch sequence and+-- shaking it (sift). Whatever falls out is the pending we want+-- to keep. We do this until the sequence looks about as clean as+-- we can get it+siftForPending :: forall prim wX wY . PrimPatch prim => FL prim wX wY -> Sealed (FL prim wX)+siftForPending simple_ps =+ if allFL (\p -> primIsAddfile p || primIsAdddir p) oldps+ then seal oldps+ else fromJust $ do+ Sealed x <- return $ sift NilFL $ reverseFL oldps+ return $ case tryToShrink x of+ ps | lengthFL ps < lengthFL oldps -> siftForPending ps+ | otherwise -> seal ps+ where+ oldps = fromMaybe simple_ps $ tryShrinkingInverse $ crudeSift simple_ps+ -- get rid of any hunk/binary patches that we can commute out the+ -- back (ie. we work our way backwards, pushing the patches down+ -- to the very end and popping them off; so in (addfile f :> hunk)+ -- 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 (ps:<:p) | primIsHunk p || primIsBinary p =+ case commuteFLorComplain (p :> sofar) of+ Right (sofar' :> _) -> sift sofar' ps+ Left _ -> sift (p:>:sofar) ps+ sift sofar (ps:<:p) = sift (p:>:sofar) ps++-- | '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 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++-- | @tentativelyRemoveFromPending p@ is used by Darcs whenever it+-- adds a patch to the repository (eg. with apply or record).+-- Think of it as one part of transferring patches from pending to+-- somewhere else.+--+-- Question (Eric Kow): how do we detect patch equivalence?+tentativelyRemoveFromPending :: forall rt p wR wU wT wX wY. (RepoPatch p)+ => Repository rt p wR wU wT+ -> UpdateWorking+ -> PatchInfoAnd rt p wX wY+ -> IO ()+tentativelyRemoveFromPending _ NoUpdateWorking _ = return ()+tentativelyRemoveFromPending repo YesUpdateWorking p = do+ Sealed pend <- readTentativePending repo+ -- Question (Eric Kow): why does pending being all simple matter for+ -- 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 isSimple pend+ then crudeSift $ effect p+ else effect p+ Sealed newpend <- return $ rmpend (progressFL "Removing from pending:" effectp)+ (unsafeCoercePStart pend)+ writeTentativePending repo (unsafeCoercePStart newpend)+ where+ -- @rmpend effect pending@ removes as much of @effect@ from @pending@+ -- as possible+ --+ -- Note that @effect@ and @pending@ must start from the same context+ -- This is not a bad thing to assume because @effect@ is a patch we want to+ -- add to the repository anyway so it'd kind of have to start from wR anyway+ --+ -- Question (Eric Kow), ok then why not+ -- @PatchInfoAnd p wR wY@ in the type signature above?+ rmpend :: FL (PrimOf p) wA wB -> FL (PrimOf p) wA wC -> Sealed (FL (PrimOf p) wB)+ rmpend NilFL x = Sealed x+ rmpend _ NilFL = Sealed NilFL+ rmpend (x:>:xs) xys | Just ys <- removeFL x xys = rmpend xs ys+ rmpend (x:>:xs) ys =+ case commuteWhatWeCanFL (x:>xs) of+ a:>x':>b -> case rmpend a ys of+ Sealed ys' -> case commute (invert (x':>:b) :> ys') of+ Just (ys'' :> _) -> seal ys''+ Nothing -> seal $ invert (x':>:b)+>+ys'+ -- DJR: I don't think this last case should be+ -- reached, but it also shouldn't lead to corruption.++-- | 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.+--+-- 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++-- | @makeNewPending repo YesUpdateWorking pendPs@ verifies that the+-- @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 :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> UpdateWorking+ -> FL (PrimOf p) wT wY+ -> Tree IO -- ^recorded state of the repository, to check if pending can be applied+ -> IO ()+makeNewPending _ NoUpdateWorking _ _ = return ()+makeNewPending repo YesUpdateWorking origp recordedState =+ withRepoLocation repo $+ do let newname = pendingName ++ ".new"+ debugMessage $ "Writing new pending: " ++ newname+ Sealed sfp <- return $ siftForPending origp+ writeNewPending repo sfp+ Sealed p <- readNewPending repo+ -- We don't ever use the resulting tree.+ _ <- catch (applyToTree p recordedState) $ \(err :: IOException) -> do+ let buggyname = pendingName ++ "_buggy"+ renameFile newname buggyname+ errorDoc $ text ("There was an attempt to write an invalid pending! " ++ show err)+ $$ text "If possible, please send the contents of"+ <+> text buggyname+ $$ text "along with a bug report."+ renameFile newname pendingName+ debugMessage $ "Finished writing new pending: " ++ newname++-- | Replace the pending patch with the tentative pending.+-- If @NoUpdateWorking@, this merely deletes the tentative pending+-- without replacing the current one.+--+-- Question (Eric Kow): shouldn't this also delete the tentative+-- pending if @YesUpdateWorking@? I'm just puzzled by the seeming+-- inconsistency of the @NoUpdateWorking@ doing deletion, but+-- @YesUpdateWorking@ not bothering.+finalizePending :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> UpdateWorking+ -> Tree IO+ -> IO ()+finalizePending repo NoUpdateWorking _ =+ withRepoLocation repo $ removeFileMayNotExist pendingName+finalizePending repo updateWorking@YesUpdateWorking recordedState =+ withRepoLocation repo $ do+ Sealed tpend <- readTentativePending repo+ Sealed new_pending <- return $ siftForPending tpend+ makeNewPending repo updateWorking new_pending recordedState++-- | @tentativelyAddToPending repo NoDryRun YesUpdateWorking pend ps@+-- appends @ps@ to the pending patch.+--+-- It has no effect with @NoUpdateWorking@.+--+-- 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 rt p wR wU wT wX wY. RepoPatch p+ => Repository rt p wR wU wT+ -> UpdateWorking+ -> FL (PrimOf p) wX wY+ -> IO ()+tentativelyAddToPending _ NoUpdateWorking _ = return ()+tentativelyAddToPending repo YesUpdateWorking patch =+ withRepoLocation repo $ do+ Sealed pend <- readTentativePending repo+ FlippedSeal newpend_ <- return $+ newpend (unsafeCoerceP pend :: FL (PrimOf p) wA wX) patch+ writeTentativePending repo (unsafeCoercePStart newpend_)+ where+ newpend :: FL prim wA wB -> FL prim wB wC -> FlippedSeal (FL prim) wC+ newpend NilFL patch_ = flipSeal patch_+ newpend p patch_ = flipSeal $ p +>+ patch_++-- | setTentativePending is basically unsafe. It overwrites the pending+-- state with a new one, not related to the repository state.+setTentativePending :: forall rt p wR wU wT wX wY. RepoPatch p+ => Repository rt p wR wU wT+ -> UpdateWorking+ -> FL (PrimOf p) wX wY+ -> IO ()+setTentativePending _ NoUpdateWorking _ = return ()+setTentativePending repo YesUpdateWorking patch = do+ Sealed prims <- return $ siftForPending patch+ withRepoLocation repo $ writeTentativePending repo (unsafeCoercePStart prims)++-- | @prepend repo YesUpdateWorking ps@ prepends @ps@ to the pending patch+-- It's used right before removing @ps@ from the repo. This ensures that+-- the pending patch can still be applied on top of the recorded state.+--+-- This function is basically unsafe. It overwrites the pending state+-- with a new one, not related to the repository state.+prepend :: forall rt p wR wU wT wX wY. RepoPatch p+ => Repository rt p wR wU wT+ -> UpdateWorking+ -> FL (PrimOf p) wX wY+ -> IO ()+prepend _ NoUpdateWorking _ = return ()+prepend repo YesUpdateWorking patch = do+ Sealed pend <- readTentativePending repo+ Sealed newpend_ <- return $ newpend (unsafeCoerceP pend) patch+ writeTentativePending repo (unsafeCoercePStart $ crudeSift newpend_)+ where+ newpend :: FL prim wB wC -> FL prim wA wB -> Sealed (FL prim wA)+ newpend NilFL patch_ = seal patch_+ newpend p patch_ = seal $ patch_ +>+ p++
@@ -15,9 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. --{-# LANGUAGE CPP #-}- module Darcs.Repository.Prefs ( addToPreflist , deleteSources@@ -39,12 +36,15 @@ , FileType(..) , filetypeFunction , getCaches- , binariesFileHelp- , boringFileHelp , globalCacheDir , globalPrefsDirDoc , globalPrefsDir- , oldGlobalCacheDir+ , getMotd+ , showMotd+ , prefsUrl+ , prefsDirPath+ -- * documentation of prefs files+ , prefsFilesHelp ) where import Prelude ()@@ -56,28 +56,28 @@ import Data.List ( nub, isPrefixOf, union, sortBy, lookup ) import Data.Maybe ( isJust, fromMaybe, mapMaybe, catMaybes, maybeToList ) import qualified Control.Exception as C-import qualified Data.ByteString as B ( empty )+import qualified Data.ByteString as B ( empty, null, hPut, ByteString ) import qualified Data.ByteString.Char8 as BC ( unpack ) import System.Directory ( getAppUserDataDirectory, doesDirectoryExist, createDirectory, doesFileExist ) import System.Environment ( getEnvironment ) import System.FilePath.Posix ( normalise, dropTrailingPathSeparator, (</>) ) import System.IO.Error ( isDoesNotExistError )-import System.IO ( stderr )+import System.IO ( stdout, stderr ) import System.Info ( os ) import Text.Regex ( Regex, mkRegex, matchRegex ) import Darcs.Repository.Cache ( Cache(..), CacheType(..), CacheLoc(..), WritableOrNot(..), compareByLocality )-import Darcs.Util.External ( gzFetchFilePS , Cachable( Cachable ))+import Darcs.Util.External ( gzFetchFilePS , fetchFilePS, Cachable(..)) import Darcs.Repository.Flags( UseCache (..), DryRun (..), SetDefault (..), RemoteRepos (..) )-import Darcs.Util.Lock( readBinFile, writeBinFile )+import Darcs.Util.Lock( readTextFile, writeTextFile ) import Darcs.Util.Exception ( catchall ) import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Path ( AbsolutePath, ioAbsolute, toFilePath, getCurrentDirectory )-import Darcs.Util.Printer( hPutDocLn, text, RenderMode(..) )+import Darcs.Util.Printer( hPutDocLn, text ) import Darcs.Util.URL ( isValidLocalPath ) import Darcs.Util.File ( osxCacheDir, xdgCacheDir, removeFileMayNotExist ) @@ -91,9 +91,8 @@ setPreflist "binaries" defaultBinaries setPreflist "motd" [] -{-# NOINLINE defaultBoring #-} defaultBoring :: [String]-defaultBoring = map ("# " ++) boringFileHelp +++defaultBoring = map ("# " ++) boringFileInternalHelp ++ [ "" , "### compiler and interpreter intermediate files" , "# haskell (ghc) interfaces"@@ -156,8 +155,6 @@ , "(^|/)SCCS($|/)" , "# darcs" , "(^|/)"++darcsdir++"($|/)", "(^|/)\\.darcsrepo($|/)"- , "^\\.darcs-temp-mail$"- , "-darcs-backup[[:digit:]]+$" , "# gnu arch" , "(^|/)(\\+|,)" , "(^|/)vssver\\.scc$"@@ -191,12 +188,12 @@ , "(^|.*/)\\.emacs\\.desktop(\\.lock)?$" ] -boringFileHelp :: [String]-boringFileHelp =+boringFileInternalHelp :: [String]+boringFileInternalHelp = [ "This file contains a list of extended regular expressions, one per" , "line. A file path matching any of these expressions will be filtered"- , "out during `darcs add', or when the `--look-for-adds' flag is passed"- , "to `darcs whatsnew' and `record'. The entries in "+ , "out during `darcs add`, or when the `--look-for-adds` flag is passed"+ , "to `darcs whatsnew` and `record`. The entries in " ++ globalPrefsDirDoc ++ "boring (if" , "it exists) supplement those in this file." , ""@@ -252,18 +249,6 @@ | osx = ((</> "darcs") `fmap`) `fmap` osxCacheDir | otherwise = ((</> "darcs") `fmap`) `fmap` xdgCacheDir --- |oldGlobalCacheDir is the old cache path @~/.darcs/cache@--- now ony used with read-only access.-oldGlobalCacheDir :: IO (Maybe FilePath)-oldGlobalCacheDir- = do dir <- ((</> "cache") `fmap`) `fmap` globalPrefsDir- case dir of- Nothing -> return Nothing- Just d -> do exists <- doesDirectoryExist d- if exists- then return $ Just d- else return Nothing- -- |tryMakeBoringRegexp attempts to create a Regex from a given String. The -- evaluation is forced, to ensure any malformed exceptions are thrown here, -- and not later.@@ -273,7 +258,7 @@ regex = C.evaluate (Just $! mkRegex input) handleBadRegex :: C.SomeException -> IO (Maybe Regex)- handleBadRegex _ = hPutDocLn Encode stderr warning >> return Nothing+ handleBadRegex _ = hPutDocLn stderr warning >> return Nothing warning = text $ "Warning: Ignored invalid boring regex: " ++ input @@ -302,10 +287,10 @@ nonComment _ = True getPrefLines :: FilePath -> IO [String]-getPrefLines f = removeCRsCommentsAndConflicts `fmap` readBinFile f+getPrefLines f = removeCRsCommentsAndConflicts `fmap` readTextFile f where removeCRsCommentsAndConflicts =- filter notconflict . noncomments . map stripCr . lines+ filter notconflict . noncomments . map stripCr startswith [] _ = True startswith (x : xs) (y : ys) = x == y && startswith xs ys startswith _ _ = False@@ -325,7 +310,6 @@ | TextFile deriving (Eq) -{-# NOINLINE defaultBinaries #-} -- | The lines that will be inserted into @_darcs/prefs/binaries@ when -- @darcs init@ is run. Hence, a list of comments, blank lines and -- regular expressions (ERE dialect).@@ -333,7 +317,7 @@ -- Note that while this matches .gz and .GZ, it will not match .gZ, -- i.e. it is not truly case insensitive. defaultBinaries :: [String]-defaultBinaries = map ("# "++) binariesFileHelp +++defaultBinaries = map ("# "++) binariesFileInternalHelp ++ [ "\\." ++ regexToMatchOrigOrUpper e ++ "$" | e <- extensions ] where regexToMatchOrigOrUpper e = "(" ++ e ++ "|" ++ map toUpper e ++ ")"@@ -363,8 +347,8 @@ , "zip" ] -binariesFileHelp :: [String]-binariesFileHelp =+binariesFileInternalHelp :: [String]+binariesFileInternalHelp = [ "This file contains a list of extended regular expressions, one per" , "line. A file path matching any of these expressions is assumed to" , "contain binary data (not text). The entries in "@@ -402,7 +386,7 @@ hasprefs <- doesDirectoryExist prefs unless hasprefs $ createDirectory prefs pl <- getPreflist pref- writeBinFile (prefs ++ pref) . unlines $ union [value] pl+ writeTextFile (prefs ++ pref) . unlines $ union [value] pl getPreflist :: String -> IO [String] getPreflist p = findPrefsDirectory >>=@@ -417,14 +401,14 @@ setPreflist p ls = withPrefsDirectory $ \prefs -> do haspref <- doesDirectoryExist prefs when haspref $- writeBinFile (prefs ++ p) (unlines ls)+ writeTextFile (prefs ++ p) (unlines ls) defPrefval :: String -> String -> IO String defPrefval p d = fromMaybe d `fmap` getPrefval p getPrefval :: String -> IO (Maybe String) getPrefval p = do- pl <- getPreflist "prefs"+ pl <- getPreflist prefsDir return $ case map snd $ filter ((== p) . fst) $ map (break (== ' ')) pl of [val] -> case words val of [] -> Nothing@@ -433,8 +417,8 @@ setPrefval :: String -> String -> IO () setPrefval p v = do- pl <- getPreflist "prefs"- setPreflist "prefs" $ updatePrefVal pl p v+ pl <- getPreflist prefsDir+ setPreflist prefsDir $ updatePrefVal pl p v updatePrefVal :: [String] -> String -> String -> [String] updatePrefVal prefList p newVal =@@ -442,10 +426,10 @@ changePrefval :: String -> String -> String -> IO () changePrefval p f t = do- pl <- getPreflist "prefs"+ pl <- getPreflist prefsDir ov <- getPrefval p let newval = maybe t (\old -> if old == f then t else old) ov- setPreflist "prefs" $ updatePrefVal pl p newval+ setPreflist prefsDir $ updatePrefVal pl p newval fixRepoPath :: String -> IO FilePath fixRepoPath p@@ -513,12 +497,7 @@ `fmap` (gzFetchFilePS (repodir </> sourcesFile) Cachable `catchall` return B.empty)- oldGlobalcachedir <- oldGlobalCacheDir globalcachedir <- globalCacheDir- let oldGlobalcache = if nocache then []- else case oldGlobalcachedir of- Nothing -> []- Just d -> [Cache Directory NotWritable d] let globalcache = if nocache then [] else case globalcachedir of@@ -530,7 +509,6 @@ thatrepo = [Cache Repo NotWritable repodir] tempCache = nub $ thisrepo ++ globalcache ++ globalsources ++ here ++ thatrepo ++ filterExternalSources there- ++ oldGlobalcache return $ Ca $ sortBy compareByLocality tempCache where sourcesFile = darcsdir ++ "/prefs/sources"@@ -551,3 +529,158 @@ if isValidLocalPath repodir then there else filter (not . isValidLocalPath . cacheSource) there++-- | Fetch and return the message of the day for a given repository.+getMotd :: String -> IO B.ByteString+getMotd repo = fetchFilePS motdPath (MaxAge 600) `catchall` return B.empty+ where+ motdPath = repo ++ "/" ++ darcsdir ++ "/prefs/motd"++-- | Display the message of the day for a given repository,+showMotd :: String -> IO ()+showMotd repo = do+ motd <- getMotd repo+ unless (B.null motd) $ do+ B.hPut stdout motd+ putStrLn $ replicate 22 '*'++prefsUrl :: FilePath -> String+prefsUrl r = r ++ "/"++darcsdir++"/prefs"++prefsDir :: FilePath+prefsDir = "prefs"++prefsDirPath :: FilePath+prefsDirPath = darcsdir </> prefsDir++prefsFilesHelp :: [(String,String)]+prefsFilesHelp =+ [ ("motd", unlines+ [ "The `_darcs/prefs/motd` file may contain a 'message of the day' which"+ , "will be displayed to users who clone or pull from the repository without"+ , "the `--quiet` option."])+ , ("email", unlines+ [ "The `_darcs/prefs/email` file is used to provide the e-mail address for"+ , "your repository that others will use when they `darcs send` a patch back"+ , "to you. The contents of the file should simply be an e-mail address."])+ , ("post", unlines+ [ "If `_darcs/prefs/post` exists in the target repository, `darcs send ` will"+ , "upload to the URL contained in that file, which may either be a `mailto:`"+ , "URL, or an `http://` URL. In the latter case, the patch is posted to that URL."])+ , ("author", unlines+ [ "The `_darcs/prefs/author` file contains the email address (or name) to"+ , "be used as the author when patches are recorded in this repository,"+ , "e.g. `David Roundy <droundy@abridgegame.org>`. This file overrides the"+ , "contents of the environment variables `$DARCS_EMAIL` and `$EMAIL`."])+ , ("defaults", unlines+ [ "Default values for darcs commands. Each line of this file has the"+ , "following form:"+ , ""+ , " COMMAND FLAG VALUE"+ , ""+ , "where `COMMAND` is either the name of the command to which the default"+ , "applies, or `ALL` to indicate that the default applies to all commands"+ , "accepting that flag. The `FLAG` term is the name of the long argument"+ , "option without the `--`, i.e. `verbose` rather than `--verbose`."+ , "Finally, the `VALUE` option can be omitted if the flag does not involve"+ , "a value. If the value has spaces in it, use single quotes, not double"+ , "quotes, to surround it. Each line only takes one flag. To set multiple"+ , "defaults for the same command (or for `ALL` commands), use multiple lines."+ , ""+ , "Note that the use of `ALL` easily can have unpredicted consequences,"+ , "especially if commands in newer versions of darcs accepts flags that"+ , "they did not in previous versions. Only use safe flags with `ALL`."+ , ""+ , "For example, if your system clock is bizarre, you could instruct darcs to"+ , "always ignore the file modification times by adding the following line:"+ , ""+ , " ALL ignore-times"+ , ""+ , "There are some options which are meant specifically for use in"+ , "`_darcs/prefs/defaults`. One of them is `--disable`. As the name"+ , "suggests, this option will disable every command that got it as"+ , "argument. So, if you are afraid that you could damage your repositories"+ , "by inadvertent use of a command like amend, add the following line:"+ , ""+ , " amend disable"+ , ""+ , "Also, a global preferences file can be created with the name"+ , "`.darcs/defaults` in your home directory. Options present there will be"+ , "added to the repository-specific preferences if they do not conflict."])+ , ("sources", unlines+ [ "The `_darcs/prefs/sources` file is used to indicate alternative locations"+ , "from which to download patches. This file contains lines such as:"+ , ""+ , " cache:/home/droundy/.cache/darcs"+ , " readonly:/home/otheruser/.cache/darcs"+ , " repo:http://darcs.net"+ , ""+ , "This would indicate that darcs should first look in"+ , "`/home/droundy/.cache/darcs` for patches that might be missing, and if"+ , "the patch is not there, it should save a copy there for future use."+ , "In that case, darcs will look in `/home/otheruser/.cache/darcs` to see if"+ , "that user might have downloaded a copy, but will not try to save a copy"+ , "there, of course. Finally, it will look in `http://darcs.net`. Note that"+ , "the `sources` file can also exist in `~/.darcs/`. Also note that the"+ , "sources mentioned in your `sources` file will be tried *before* the"+ , "repository you are pulling from. This can be useful in avoiding"+ , "downloading patches multiple times when you pull from a remote"+ , "repository to more than one local repository."+ , ""+ , "A global cache is enabled by default in your home directory. The cache"+ , "allows darcs to avoid re-downloading patches (for example, when doing a"+ , "second darcs clone of the same repository), and also allows darcs to use"+ , "hard links to reduce disk usage."+ , ""+ , "Note that the cache directory should reside on the same filesystem as"+ , "your repositories, so you may need to vary this. You can also use"+ , "multiple cache directories on different filesystems, if you have several"+ , "filesystems on which you use darcs."])+ , ("boring", unlines+ [ "The `_darcs/prefs/boring` file may contain a list of regular expressions"+ , "describing files, such as object files, that you do not expect to add to"+ , "your project. A newly created repository has a boring file that includes"+ , "many common source control, backup, temporary, and compiled files."+ , ""+ , "You may want to have the boring file under version control. To do this"+ , "you can use darcs setpref to set the value 'boringfile' to the name of"+ , "your desired boring file (e.g. `darcs setpref boringfile .boring`, where"+ , "`.boring` is the repository path of a file that has been darcs added to"+ , "your repository). The boringfile preference overrides"+ , "`_darcs/prefs/boring`, so be sure to copy that file to the boringfile."+ , ""+ , "You can also set up a 'boring' regexps file in your home directory, named"+ , "`~/.darcs/boring`, which will be used with all of your darcs repositories."+ , ""+ , "Any file not already managed by darcs and whose repository path"+ , "matches any of the boring regular expressions is"+ , "considered boring. The boring file is used to filter the files provided"+ , "to darcs add, to allow you to use a simple `darcs add newdir newdir/*`"+ , "without accidentally adding a bunch of object files. It is also used"+ , "when the `--look-for-adds` flag is given to whatsnew or record. Note"+ , "that once a file has been added to darcs, it is not considered boring,"+ , "even if it matches the boring file filter."])+ , ("binaries", unlines+ [ "The `_darcs/prefs/binaries` file may contain a list of regular"+ , "expressions describing files that should be treated as binary files rather"+ , "than text files. Darcs automatically treats files containing characters"+ , "`^Z` or `NULL` within the first 4096 bytes as being binary files."+ , "You probably will want to have the binaries file under version control."+ , "To do this you can use `darcs setpref` to set the value 'binariesfile'"+ , "to the name of your desired binaries file"+ , "(e.g. `darcs setpref binariesfile ./.binaries`, where `.binaries` is a"+ , "file that has been darcs added to your repository). As with the boring"+ , "file, you can also set up a `~/.darcs/binaries` file if you like."])+ , ("defaultrepo", unlines+ [ "Contains the URL of the default remote repository used by commands `pull`,"+ , "`push`, `send` and `optimize relink`. Darcs edits this file automatically"+ , "or when the flag `--set-default` is used."])+ , ("tmpdir", unlines+ [ "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`."])+ , ("prefs", unlines+ [ "Contains the preferences set by the command `darcs setprefs`."+ , "Do not edit manually."])+ ]
@@ -1,7 +1,6 @@ -- Copyright (C) 2009-2012 Ganesh Sittampalam -- -- BSD3-{-# LANGUAGE CPP #-} module Darcs.Repository.Rebase ( RebaseJobFlags(..) , withManualRebaseUpdate@@ -52,7 +51,7 @@ , removeFromFormat , writeRepoFormat )-import Darcs.Repository.Internal+import Darcs.Repository.Hashed ( tentativelyAddPatch , tentativelyAddPatch_ , tentativelyAddPatches_@@ -64,19 +63,17 @@ , readRepo , UpdatePristine(..) )-import Darcs.Repository.InternalTypes ( Repository(..) )+import Darcs.Repository.InternalTypes ( Repository, repoFormat, repoLocation ) import qualified Darcs.Util.Diff as D ( DiffAlgorithm(MyersDiff) )+import Darcs.Util.Printer ( ePutDocLn, text ) import Darcs.Util.Progress ( debugMessage )- import Darcs.Util.Tree ( Tree ) 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@@ -98,7 +95,7 @@ -> IO (Repository rt p wR wU wT2, x) withManualRebaseUpdate (RebaseJobFlags compr verb uw) r subFunc | SRepoType SIsRebase <- singletonRepoType :: SRepoType rt- = do patches <- readTentativeRepo r+ = do patches <- readTentativeRepo r (repoLocation r) 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)) =@@ -151,9 +148,8 @@ => Repository ('RepoType 'IsRebase) p wR wU wR -> RebaseJobFlags -> IO ()-checkSuspendedStatus repo@(Repo _ rf _ _) flags@(RebaseJobFlags compr _verb uw) = do- allpatches <- readRepo repo- (_, Sealed2 ps) <- return $ takeAnyRebase allpatches+checkSuspendedStatus repo flags@(RebaseJobFlags compr _verb uw) = do+ (_, Sealed2 ps) <- takeAnyRebase <$> readRepo repo case countToEdit ps of 0 -> do debugMessage "Removing the rebase patch file..."@@ -168,9 +164,11 @@ (rebase, _, _) <- takeHeadRebase <$> readRepo repo' repo'' <- tentativelyRemovePatches repo' compr uw (rebase :>: NilFL) finalizeRepositoryChanges repo'' uw compr- writeRepoFormat (removeFromFormat RebaseInProgress rf) (darcsdir </> "format")+ writeRepoFormat+ (removeFromFormat RebaseInProgress (repoFormat repo))+ (darcsdir </> "format") putStrLn "Rebase finished!"- n -> putStrLn $ "Rebase in progress: " ++ show n ++ " suspended patches"+ n -> ePutDocLn $ text $ "Rebase in progress: " ++ show n ++ " suspended patches" moveRebaseToEnd :: (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR@@ -191,14 +189,13 @@ finalizeRepositoryChanges repo'''' uw compr return repo'''' -displaySuspendedStatus :: (RepoPatch p, ApplyState p ~ Tree) => Repository ('RepoType 'IsRebase) p wR wU wR -> IO ()+displaySuspendedStatus :: RepoPatch p => 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"+ (_, Sealed2 ps) <- takeAnyRebase <$> readRepo repo+ ePutDocLn $ text $ "Rebase in progress: " ++ show (countToEdit ps) ++ " suspended patches" maybeDisplaySuspendedStatus- :: (RepoPatch p, ApplyState p ~ Tree)+ :: RepoPatch p => SRebaseType rebaseType -> Repository ('RepoType rebaseType) p wR wU wR -> IO () maybeDisplaySuspendedStatus SIsRebase repo = displaySuspendedStatus repo maybeDisplaySuspendedStatus SNoRebase _ = return ()@@ -207,7 +204,9 @@ => Repository ('RepoType 'IsRebase) p wR wU wT -> RebaseJobFlags -> IO (Repository ('RepoType 'IsRebase) p wR wU wT)-startRebaseIfNecessary repo@(Repo _ rf _ _) (RebaseJobFlags compr verb uw) =+startRebaseIfNecessary repo (RebaseJobFlags compr verb uw) =+ let rf = repoFormat repo+ in if formatHas RebaseInProgress rf then return repo else do -- TODO this isn't under the repo lock, and it should be
@@ -1,5 +1,3 @@-{-# LANGUAGE CPP, PatternGuards #-}- module Darcs.Repository.Repair ( replayRepository, checkIndex, replayRepositoryInTemp, RepositoryConsistency(..) )@@ -26,8 +24,8 @@ import Darcs.Patch.Witnesses.Sealed ( Sealed2(..), Sealed(..), unFreeLeft ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Repair ( Repair(applyAndTryToFix) )-import Darcs.Patch.Info ( showPatchInfoUI )-import Darcs.Patch.Set ( Origin, PatchSet(..), newset2FL, newset2RL )+import Darcs.Patch.Info ( displayPatchInfo )+import Darcs.Patch.Set ( Origin, PatchSet(..), patchSet2FL, patchSet2RL ) import Darcs.Patch ( RepoPatch, IsRepoType, PrimOf, isInconsistent ) import Darcs.Repository.Flags@@ -36,10 +34,10 @@ RepoProperty ( HashedInventory ), formatHas ) import Darcs.Repository.Cache ( HashedDir( HashedPristineDir ) ) import Darcs.Repository.HashedIO ( cleanHashdir )-import Darcs.Repository.HashedRepo ( readHashedPristineRoot, writeAndReadPatch )-import Darcs.Repository.InternalTypes ( Repository(..), extractCache )+import Darcs.Repository.Hashed ( readHashedPristineRoot, writeAndReadPatch )+import Darcs.Repository.InternalTypes ( Repository, repoCache, repoLocation ) import Darcs.Repository.Prefs ( filetypeFunction )-import Darcs.Repository.Internal ( readRepo )+import Darcs.Repository.Hashed ( readRepo ) import Darcs.Repository.State ( readRecorded , readIndex@@ -52,7 +50,7 @@ import Darcs.Util.Exception ( catchall ) import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Lock( rmRecursive, withTempDir )-import Darcs.Util.Printer ( Doc, putDocLn, text, RenderMode(..) )+import Darcs.Util.Printer ( Doc, putDocLn, text ) import Darcs.Util.Printer.Color ( showDoc ) import Darcs.Util.Hash( Hash(NoHash), encodeBase16 )@@ -62,9 +60,7 @@ import Darcs.Util.Tree.Plain( readPlainTree ) import Darcs.Util.Index( updateIndex ) -import qualified Data.ByteString.Char8 as BS--#include "impossible.h"+import qualified Data.ByteString.Char8 as BC replaceInFL :: FL (PatchInfoAnd rt a) wX wY -> [Sealed2 (WPatchInfo :||: PatchInfoAnd rt a)]@@ -79,12 +75,12 @@ => 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 =+applyAndFix r compr psin = do liftIO $ beginTedious k liftIO $ tediousSize k $ lengthFL psin (repaired, ok) <- aaf psin liftIO $ endTedious k- orig <- liftIO $ newset2FL `fmap` readRepo r+ orig <- liftIO $ patchSet2FL `fmap` readRepo r return (replaceInFL orig repaired, ok) where k = "Replaying patch" aaf :: FL (PatchInfoAnd rt p) wW wZ -> TreeIO ([Sealed2 (WPatchInfo :||: PatchInfoAnd rt p)], Bool)@@ -95,28 +91,30 @@ Just err -> liftIO $ putDocLn err Nothing -> return () let !winfp = winfo p -- assure that 'p' can be garbage collected.- liftIO $ finishedOneIO k $ showDoc Encode $ showPatchInfoUI $ unWPatchInfo winfp+ liftIO $ finishedOneIO k $ showDoc $ displayPatchInfo $ unWPatchInfo winfp (ps', restok) <- aaf ps case mp' of Nothing -> return (ps', restok)- Just (e,pp) -> liftIO $ do putStrLn e- p' <- withCurrentDirectory r' $ writeAndReadPatch c compr pp- return (Sealed2 (winfp :||: p'):ps', False)+ Just (e,pp) -> liftIO $ do+ putStrLn e+ p' <- withCurrentDirectory (repoLocation r) $+ writeAndReadPatch (repoCache r) compr pp+ return (Sealed2 (winfp :||: p'):ps', False) data RepositoryConsistency rt p wX = RepositoryConsistent | BrokenPristine (Tree IO) | BrokenPatches (Tree IO) (PatchSet rt p Origin wX) -checkUniqueness :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+checkUniqueness :: (IsRepoType rt, RepoPatch p) => (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- case hasDuplicate $ mapRL info $ newset2RL r of+ case hasDuplicate $ mapRL info $ patchSet2RL r of Nothing -> return () Just pinf -> do putInfo $ text "Error! Duplicate patch name:"- putInfo $ showPatchInfoUI pinf+ putInfo $ displayPatchInfo pinf fail "Duplicate patches found." hasDuplicate :: Ord a => [a] -> Maybe a@@ -139,7 +137,7 @@ putVerbose $ text "Applying patches..." patches <- readRepo repo debugMessage "Fixing any broken patches..."- let psin = newset2FL patches+ let psin = patchSet2FL patches repair = applyAndFix repo compr psin ((ps, patches_ok), newpris) <- hashedTreeIO repair emptyTree whereToReplay@@ -161,7 +159,7 @@ cleanupRepositoryReplay :: Repository rt p wR wU wT -> IO () cleanupRepositoryReplay r = do- let c = extractCache r+ let c = repoCache r rf <- identifyRepoFormat "." unless (formatHas HashedInventory rf) $ rmRecursive $ darcsdir ++ "/pristine.hashed"@@ -207,8 +205,8 @@ format paths = unlines $ map ((" " ++) . anchorPath "") paths mismatches_disp = unlines [ anchorPath "" p ++- "\n index: " ++ BS.unpack (encodeBase16 h1) ++- "\n working: " ++ BS.unpack (encodeBase16 h2)+ "\n index: " ++ BC.unpack (encodeBase16 h1) +++ "\n working: " ++ BC.unpack (encodeBase16 h2) | (p, h1, h2) <- mismatches ] unless (quiet || null index_extra) $ putStrLn $ "Extra items in index!\n" ++ format index_extra
@@ -15,9 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}-- module Darcs.Repository.Resolution ( standardResolution , externalResolution
@@ -25,7 +25,7 @@ ( restrictSubpaths, restrictBoring, TreeFilter(..), restrictDarcsdir , maybeRestrictSubpaths -- * Diffs- , unrecordedChanges, unrecordedChangesWithPatches, readPending+ , unrecordedChanges, readPending -- * Trees , readRecorded, readUnrecorded, readRecordedAndPending, readWorking , readPendingAndWorking, readUnrecordedFiltered@@ -33,16 +33,17 @@ , readIndex, updateIndex, invalidateIndex, UseIndex(..), ScanKnown(..) -- * Utilities , filterOutConflicts- -- * Detection of changes- , getMovesPs, getReplaces+ -- * Pending-related functions that depend on repo state+ , addPendingDiffToPending, addToPending ) where import Prelude () import Darcs.Prelude -import Control.Monad( when, foldM )+import Control.Monad ( when, foldM, forM )+import Control.Monad.State ( StateT, runStateT, get, put, liftIO ) import Control.Exception ( catch, IOException )-import Data.Maybe ( isJust, fromJust )+import Data.Maybe ( isJust ) import Data.Ord ( comparing ) import Data.List ( sortBy, union, delete ) import Text.Regex( matchRegex )@@ -50,45 +51,46 @@ import System.Directory( removeFile, doesFileExist, doesDirectoryExist, renameFile ) import System.FilePath ( (</>) ) import qualified Data.ByteString as B- ( readFile, drop, writeFile, empty, concat )+ ( ByteString, 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 ( effect, RepoPatch, PrimOf, sortCoalesceFL, fromPrim, fromPrims- , PrimPatch, primIsHunk, maybeApplyToTree+import Darcs.Patch ( RepoPatch, PrimOf, sortCoalesceFL, fromPrims+ , PrimPatch, 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+import Darcs.Patch.Witnesses.Ordered ( RL(..), FL(..), (+>+) , (:>)(..), reverseRL, reverseFL- , mapFL, concatFL, toFL )+ , mapFL, concatFL, toFL, nullFL ) import Darcs.Patch.Witnesses.Eq ( EqCheck(IsEq, NotEq) )-import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP )+import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP, unsafeCoercePEnd ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed), seal, unFreeLeft, mapSeal , freeGap, emptyGap, joinGap, FreeLeft, Gap(..) )-import Darcs.Patch.Commute ( selfCommuter )+import Darcs.Patch.Commute ( selfCommuter, commuteFL ) import Darcs.Patch.CommuteFn ( commuterIdRL )-import Darcs.Patch.Permutations ( partitionConflictingFL, partitionRL )+import Darcs.Patch.Permutations ( partitionConflictingFL, genCommuteWhatWeCanRL ) 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.FileHunk ( FileHunk(..), IsHunk(..) ) import Darcs.Patch.TokenReplace ( breakToTokens, defaultToks ) -import Darcs.Repository.Flags ( UseIndex(..), ScanKnown(..), DiffAlgorithm(..) )+import Darcs.Repository.Flags ( UseIndex(..), ScanKnown(..), DiffAlgorithm(..)+ , UpdateWorking(..), LookForMoves(..), LookForReplaces(..) ) import Darcs.Util.Global ( darcsdir ) -import Darcs.Repository.InternalTypes ( Repository(..) )+import Darcs.Repository.InternalTypes ( Repository, repoFormat ) import Darcs.Repository.Format(formatHas, RepoProperty(NoWorkingDir)) import qualified Darcs.Repository.Pending as Pending import Darcs.Repository.Prefs ( filetypeFunction, boringRegexps ) import Darcs.Repository.Diff ( treeDiff ) -import Darcs.Util.Path( AnchoredPath(..), anchorPath, floatPath, Name(..), fn2fp,- SubPath, sp2fn, filterPaths- , parents, replacePrefixPath, anchoredRoot- , toFilePath, simpleSubPath, normPath, floatSubPath )+import Darcs.Util.Path+ ( AnchoredPath(..), anchorPath, floatPath, fn2fp+ , SubPath, sp2fn, filterPaths, FileName+ , parents, replacePrefixPath, anchoredRoot+ , toFilePath, simpleSubPath, normPath, floatSubPath, makeName+ ) import Darcs.Util.Hash( Hash( NoHash ) ) import Darcs.Util.Tree( Tree, restrict, FilterTree, expand, emptyTree, overlay, find , ItemType(..), itemType, readBlob, modifyTree, findFile, TreeItem(..)@@ -101,23 +103,26 @@ newtype TreeFilter m = TreeFilter { applyTreeFilter :: forall tr . FilterTree tr m => tr m -> tr m } --- TODO: We wrap the pending patch inside RepoPatch here, to avoid the--- requirement to propagate an (ApplyState (PrimOf p) ~ ApplyState p)--- 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 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 rt p m wR wU wT. (RepoPatch p, ApplyState p ~ Tree)+restrictSubpaths :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> [SubPath] -> IO (TreeFilter m) restrictSubpaths repo subpaths = do- Sealed pending <- readPendingLL repo+ Sealed pending <- Pending.readPending repo+ restrictSubpathsAfter pending repo subpaths++-- | Like 'restrictSubpaths' but with the pending patch passed as a parameter.+-- The 'Repository' parameter is not used, we need it only to avoid+-- abiguous typing of @p@.+restrictSubpathsAfter :: (RepoPatch p, ApplyState p ~ Tree)+ => FL (PrimOf p) wT wP+ -> Repository rt p wR wU wT+ -> [SubPath]+ -> IO (TreeFilter m)+restrictSubpathsAfter pending _repo subpaths = do let paths = map (fn2fp . sp2fn) subpaths paths' = paths `union` effectOnFilePaths pending paths anchored = map floatPath paths'@@ -125,28 +130,26 @@ 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+maybeRestrictSubpaths :: (RepoPatch p, ApplyState p ~ Tree)+ => FL (PrimOf p) wT wP+ -> Repository rt p wR wU wT -> Maybe [SubPath] -> IO (TreeFilter m)-maybeRestrictSubpaths repo = maybe (return $ TreeFilter id) (restrictSubpaths repo)+maybeRestrictSubpaths pending repo =+ maybe (return $ TreeFilter id) (restrictSubpathsAfter pending repo) -- |Is the given path in (or equal to) the _darcs metadata directory? inDarcsDir :: AnchoredPath -> Bool-inDarcsDir (AnchoredPath (Name x:_)) | x == BC.pack darcsdir = True+inDarcsDir (AnchoredPath (x:_)) | x == makeName darcsdir = True inDarcsDir _ = False --- | Construct a Tree filter that removes any boring files the Tree might have--- contained. Additionally, you should (in most cases) pass an (expanded) Tree--- that corresponds to the recorded content of the repository. This is--- important in the cases when the repository contains files that would be--- boring otherwise. (If you pass emptyTree instead, such files will simply be--- discarded by the filter, which is usually not what you want.)+-- | Construct a 'TreeFilter' that removes any boring files that are not also+-- contained in the argument 'Tree'. ----- 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.-restrictBoring :: forall m . Tree m -> IO (TreeFilter m)+-- The standard use case is for the argument to be the recorded state, possibly+-- with further patches applied, so as not to discard any files already known+-- to darcs. The result is usually applied to the full working state.+restrictBoring :: Tree m -> IO (TreeFilter m) restrictBoring guide = do boring <- boringRegexps let boring' p | inDarcsDir p = False@@ -160,99 +163,120 @@ -- | Construct a Tree filter that removes any darcs metadata files the -- Tree might have contained.-restrictDarcsdir :: forall m . TreeFilter m+restrictDarcsdir :: TreeFilter m 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--- recorded state of the repository (pristine) to the unrecorded state of the--- repository (the working copy + pending). When a list of paths is given, at--- least the files that live under any of these paths in either recorded or--- unrecorded will be included in the resulting patch. NB. More patches may be--- included in this list, eg. the full contents of the pending patch. This is--- usually not a problem, since selectChanges will properly filter the results--- anyway.------ This also depends on the options given: with LookForAdds, we will include--- any non-boring files (i.e. also those that do not exist in the "recorded"--- state) in the working in the "unrecorded" state, and therefore they will--- show up in the patches as addfiles.------ The IgnoreTimes option disables index usage completely -- for each file, we--- read both the unrecorded and the recorded copy and run a diff on them. This--- 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 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 = unrecordedChangesWithPatches NilFL NilFL+{- |+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+recorded state of the repository (pristine) to the unrecorded state of the+repository (the working copy + pending). When a list of paths is given, at+least the files that live under any of these paths in either recorded or+unrecorded will be included in the resulting patch. NB. More patches may be+included in this list, eg. the full contents of the pending patch. This is+usually not a problem, since selectChanges will properly filter the results+anyway. -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 movPs replPs opts r paths = do- (pending :> working) <- readPendingAndWorkingWithPatches movPs replPs opts r paths- return $ sortCoalesceFL (pending +>+ unsafeCoerceP (movPs +>+ replPs) +>+ working)+This also depends on the options given: --- | Mostly a helper function to 'unrecordedChangesWithPatches', returning the pending--- patch plus `patches` and the subsequent diff from working as two different 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- IsEq <- return $ workDirLessRepoWitness r- return (NilFL :> NilFL)-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+--look-for-moves: Detect pending file moves using the index. The resulting+ patches are added to pending and taken into consideration, when filtering+ the tree according to the given path list. - relevant <- maybeRestrictSubpaths repo mbpaths- let getIndex = applyToTree movPs =<< I.updateIndex =<< (applyTreeFilter relevant <$> readIndex repo)- current = applyTreeFilter relevant all_current_with_patches+--look-for-adds: Include files in the working state that do not exist in the+ recorded + pending state. - 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)+--include-boring: Include even boring files. -readPendingAndWorking :: forall rt p wR wU wT . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+--look-for-replaces: Detect pending replace patches. Like detected moves,+ these are added to the pending patch. Note that, like detected moves,+ these are mere proposals for the user to consider or reject.++--ignore-times: Disables index usage completely -- for each file, we read+ both the unrecorded and the recorded copy and run a diff on them. This 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).++ Note that use of the index is also disabled when we detect moves or+ replaces, since this implies that the index is out of date.+-}+unrecordedChanges :: (RepoPatch p, ApplyState p ~ Tree)+ => (UseIndex, ScanKnown, DiffAlgorithm)+ -> LookForMoves+ -> LookForReplaces+ -> Repository rt p wR wU wT+ -> Maybe [SubPath] -> IO (FL (PrimOf p) wT wU)+unrecordedChanges dopts lfm lfr r paths = do+ (pending :> working) <- readPendingAndWorking dopts lfm lfr r paths+ return $ sortCoalesceFL (pending +>+ working)++-- Implementation note: it is important to do things in the right order: we+-- first have to read the pending patch, then detect moves, then detect adds,+-- then detect replaces.+readPendingAndWorking :: forall rt p wR wU wT. (RepoPatch p, ApplyState p ~ Tree) => (UseIndex, ScanKnown, DiffAlgorithm)+ -> LookForMoves+ -> LookForReplaces -> Repository rt p wR wU wT -> Maybe [SubPath] -> IO ((FL (PrimOf p) :> FL (PrimOf p)) wT wU)-readPendingAndWorking = readPendingAndWorkingWithPatches NilFL NilFL+readPendingAndWorking _ _ _ r _ | formatHas NoWorkingDir (repoFormat r) = do+ IsEq <- return $ workDirLessRepoWitness r+ return (NilFL :> NilFL)+readPendingAndWorking (useidx, scan, diffalg) lfm lfr repo mbpaths = do+ (pending_tree, working_tree, pending) <-+ readPendingAndMovesAndUnrecorded repo useidx scan lfm mbpaths+ (pending_tree_with_replaces, Sealed replaces) <-+ getReplaces lfr diffalg repo pending_tree working_tree+ ft <- filetypeFunction+ wrapped_diff <- treeDiff diffalg ft pending_tree_with_replaces working_tree+ case unFreeLeft wrapped_diff of+ Sealed diff -> do+ return (pending +>+ unsafeCoercePEnd replaces :> unsafeCoercePEnd diff) --- | @filteredWorking useidx scan relevant getIndex pending_tree@ reads the+readPendingAndMovesAndUnrecorded+ :: (RepoPatch p, ApplyState p ~ Tree)+ => Repository rt p wR wU wT+ -> UseIndex+ -> ScanKnown+ -> LookForMoves+ -> Maybe [SubPath]+ -> IO ( Tree IO -- pristine with (pending + moves)+ , Tree IO -- working+ , FL (PrimOf p) wT wU -- pending + moves+ )+readPendingAndMovesAndUnrecorded repo useidx scan lfm mbpaths = do+ (pending_tree, Sealed pending) <- readPending repo+ moves <- getMoves lfm repo mbpaths+ let pending' = pending +>+ moves+ relevant <- maybeRestrictSubpaths pending' repo mbpaths+ pending_tree' <-+ applyTreeFilter relevant <$> applyToTree moves pending_tree+ let useidx' = if nullFL moves then useidx else IgnoreIndex+ index <-+ applyToTree moves =<< I.updateIndex =<<+ applyTreeFilter relevant <$> readIndex repo+ working_tree <- filteredWorking useidx' scan relevant index pending_tree'+ return (pending_tree', working_tree, unsafeCoercePEnd pending')++-- | @filteredWorking useidx scan relevant index 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+-- Note that even if @useidx '==' 'IgnoreIndex'@, the index is still used+-- to avoid filtering boring files that darcs knows about (see 'restrictBoring'). filteredWorking :: UseIndex- -> ScanKnown- -> TreeFilter IO- -> IO (Tree IO)- -> Tree IO- -> IO (Tree IO)-filteredWorking useidx scan relevant getIndex pending_tree = do- index <- getIndex+ -> ScanKnown+ -> TreeFilter IO+ -> Tree IO+ -> Tree IO+ -> IO (Tree IO)+filteredWorking useidx scan relevant index pending_tree = do applyTreeFilter restrictDarcsdir <$> case scan of ScanKnown -> case useidx of- UseIndex -> getIndex+ UseIndex -> return index IgnoreIndex -> do guide <- expand pending_tree applyTreeFilter relevant . restrict guide <$> readPlainTree "."@@ -271,14 +295,14 @@ -- | 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 rt p wR wU wT -> EqCheck wU wT-workDirLessRepoWitness (Repo _ rf _ _)- | formatHas NoWorkingDir rf = unsafeCoerceP IsEq- | otherwise = NotEq+workDirLessRepoWitness r+ | formatHas NoWorkingDir (repoFormat r) = unsafeCoerceP IsEq+ | otherwise = NotEq -- | 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.-readRecorded :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> IO (Tree IO)+readRecorded :: Repository rt p wR wU wT -> IO (Tree IO) readRecorded _repo = do let h_inventory = darcsdir </> "hashed_inventory" hashed <- doesFileExist h_inventory@@ -290,7 +314,8 @@ (pris_line:_) -> do 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+ when (hash == NoHash) $+ fail $ "Bad pristine root: " ++ show pris_line readDarcsHashed (darcsdir </> "pristine.hashed") (size, hash) else do have_pristine <- doesDirectoryExist $ darcsdir </> "pristine" have_current <- doesDirectoryExist $ darcsdir </> "current"@@ -309,7 +334,8 @@ readUnrecorded :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> Maybe [SubPath] -> IO (Tree IO) readUnrecorded repo mbpaths = do- relevant <- maybeRestrictSubpaths repo mbpaths+ Sealed pending <- Pending.readPending repo+ relevant <- maybeRestrictSubpaths pending repo mbpaths readIndex repo >>= I.updateIndex . applyTreeFilter relevant -- | A variant of 'readUnrecorded' that takes the UseIndex and ScanKnown@@ -320,28 +346,25 @@ => Repository rt p wR wU wT -> UseIndex -> ScanKnown+ -> LookForMoves -> 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+readUnrecordedFiltered repo useidx scan lfm mbpaths = do+ (_, working_tree, _) <-+ readPendingAndMovesAndUnrecorded repo useidx scan lfm mbpaths+ return working_tree -- | 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 /= BC.pack darcsdir)+readWorking = expand =<< (applyTreeFilter restrictDarcsdir <$> readPlainTree ".") --- | Obtains the same Tree as 'readRecorded' would but with the additional side--- effect of reading/checking the pending patch.+-- | Obtains the recorded 'Tree' with the pending patch applied. readRecordedAndPending :: (RepoPatch p, ApplyState p ~ Tree) => 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+-- | Obtains the recorded 'Tree' with the pending patch applied, plus+-- the pending patch itself. 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.@@ -349,84 +372,99 @@ -- Question (Eric Kow) Is this a bug? Darcs.Repository.Pending.readPending -- says it is readPending :: (RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT -> IO (Tree IO, Sealed (FL p wT))-readPending repo =- do Sealed pending <- readPendingLL repo- pristine <- readRecorded repo- catch ((\t -> (t, seal pending)) `fmap` applyToTree pending pristine) $ \ (err :: IOException) -> do+ => Repository rt p wR wU wT+ -> IO (Tree IO, Sealed (FL (PrimOf p) wT))+readPending repo = do+ pristine <- readRecorded repo+ Sealed pending <- Pending.readPending repo+ catch ((\t -> (t, seal pending)) <$> applyToTree pending pristine) $+ \(err :: IOException) -> do putStrLn $ "Yikes, pending has conflicts! " ++ show err putStrLn "Stashing the buggy pending as _darcs/patches/pending_buggy" renameFile (darcsdir </> "patches" </> "pending") (darcsdir </> "patches" </> "pending_buggy") return (pristine, seal NilFL) +index_file, index_invalid :: FilePath+index_file = darcsdir </> "index"+index_invalid = darcsdir </> "index_invalid"+ -- | Mark the existing index as invalid. This has to be called whenever the -- listing of pristine changes and will cause darcs to update the index next -- time it tries to read it. (NB. This is about files added and removed from -- pristine: changes to file content in either pristine or working are handled -- transparently by the index reading code.) invalidateIndex :: t -> IO ()-invalidateIndex _ = B.writeFile (darcsdir </> "index_invalid") B.empty+invalidateIndex _ = B.writeFile index_invalid B.empty -readIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt 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) $ removeFile $ darcsdir </> "index"+ (invalid, exists, formatValid) <- checkIndex if not exists || invalid || not formatValid then do pris <- readRecordedAndPending repo- idx <- I.updateIndexFrom (darcsdir </> "index") darcsTreeHash pris- when invalid $ removeFile $ darcsdir </> "index_invalid"+ idx <- I.updateIndexFrom index_file darcsTreeHash pris+ when invalid $ removeFile index_invalid return idx- else I.readIndex (darcsdir </> "index") darcsTreeHash+ else I.readIndex index_file darcsTreeHash -updateIndex :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt 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"+ (invalid, _, _) <- checkIndex+ pris <- readRecordedAndPending repo+ _ <- I.updateIndexFrom index_file darcsTreeHash pris+ when invalid $ removeFile index_invalid++checkIndex :: IO (Bool, Bool, Bool)+checkIndex = do+ invalid <- doesFileExist $ index_invalid+ exists <- doesFileExist index_file+ formatValid <- if exists+ then I.indexFormatValid index_file else return True- when (exists && not formatValid) $ removeFile $ darcsdir </> "index"- pris <- readRecordedAndPending repo- _ <- I.updateIndexFrom (darcsdir </> "index") darcsTreeHash pris- when invalid $ removeFile $ darcsdir </> "index_invalid"+ when (exists && not formatValid) $ removeFile index_file+ return (invalid, exists, formatValid) -- |Remove any patches (+dependencies) from a sequence that -- conflict with the recorded or unrecorded changes in a repo-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 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+ :: (RepoPatch p, ApplyState p ~ Tree)+ => RL (PatchInfoAnd rt p) wX wT -- ^Recorded patches from repository, starting from+ -- same context as the patches to filter+ -> 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+ =<< unrecordedChanges (UseIndex, ScanKnown, MyersDiff)+ NoLookForMoves NoLookForReplaces repository Nothing 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 --- |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+-- | Automatically detect file moves using the index.+-- TODO: This function lies about the witnesses.+getMoves :: forall rt p wR wU wT wB prim.+ (RepoPatch p, ApplyState p ~ Tree, prim ~ PrimOf p)+ => LookForMoves+ -> Repository rt p wR wU wT+ -> Maybe [SubPath]+ -> IO (FL prim wB wB)+getMoves NoLookForMoves _ _ = return NilFL+getMoves YesLookForMoves 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+ getMovedFiles :: Repository rt p wR wU wT -> Maybe [SubPath] -> IO [(AnchoredPath, AnchoredPath, ItemType)] getMovedFiles repo fs = do@@ -439,19 +477,23 @@ 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+ 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+ 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 :: [(AnchoredPath, AnchoredPath, ItemType)]+ -> [(AnchoredPath, AnchoredPath, ItemType)] resolve xs = fixPaths $ sortMoves $ deleteCycles xs where -- Input relation is left-and-right-unique. Makes cycle detection easier.@@ -495,66 +537,120 @@ -- | 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+getReplaces :: forall rt p wR wU wT+ . (RepoPatch p, ApplyState p ~ Tree)+ => LookForReplaces+ -> DiffAlgorithm+ -> Repository rt p wR wU wT+ -> Tree IO -- ^ pending tree (including possibly detected moves)+ -> Tree IO -- ^ working tree+ -> IO (Tree IO, -- new pending tree+ Sealed (FL (PrimOf p) wU))+getReplaces NoLookForReplaces _ _ pending _ = return (pending, Sealed NilFL)+getReplaces YesLookForReplaces diffalg _repo pending working = do ftf <- filetypeFunction-- Sealed changes <- unFreeLeft <$> treeDiff dopts ftf pending working- _ :> hunks <- return $ partitionRL primIsHunk $ reverseFL changes- let allModifiedTokens = concat $ mapFL modifiedTokens (reverseRL hunks)+ Sealed changes <- unFreeLeft <$> treeDiff diffalg ftf pending working+ let allModifiedTokens = concat $ mapFL modifiedTokens changes 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"+ (patches, new_pending) <-+ flip runStateT pending $+ forM replaces $ \(f,a,b) ->+ doReplace defaultToks+ (fromJust $ simpleSubPath $ fn2fp $ normPath f)+ (BC.unpack a) (BC.unpack b)+ return (new_pending, mapSeal concatFL $ toFL patches)+ where+ modifiedTokens :: PrimOf p wX wY -> [(FileName, B.ByteString, B.ByteString)]+ modifiedTokens p = case isHunk p of+ Just (FileHunk f _ old new) ->+ 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))+ Nothing -> [] - -- from a pair of token lists, create a pair of modified token lists- checkModified = filter (\(a,b) -> a/=b) . uncurry zip+ -- 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)+ 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+ doReplace toks f old new = do+ pend <- get+ mpend' <- liftIO $ maybeApplyToTree replacePatch pend+ case mpend' of+ Nothing -> getForceReplace f toks old new+ Just pend' -> do+ put pend'+ return $ joinGap (:>:) (freeGap replacePatch) (emptyGap NilFL)+ 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+ getForceReplace :: (PrimPatch prim, ApplyState prim ~ Tree)+ => SubPath -> String -> String -> String+ -> StateT (Tree IO) IO (FreeLeft (FL prim))+ getForceReplace f toks old new = do+ let path = floatSubPath f+ -- the tree here is the "current" pending state+ tree <- get+ -- 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 <- liftIO $ expandPath tree path+ content <- case findFile expandedTree path of+ Just blob -> liftIO $ readBlob blob+ Nothing -> bug $ "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 <- liftIO $ filetypeFunction+ normaliseNewTokPatch <- liftIO $ treeDiff diffalg ftf expandedTree tree'+ -- make sure we can apply them to the pending state+ patches <- return $ joinGap (+>+) normaliseNewTokPatch $ freeGap $+ tokreplace (toFilePath f) toks old new :>: NilFL+ mtree'' <- case unFreeLeft patches of+ Sealed ps -> liftIO $ maybeApplyToTree ps tree+ case mtree'' of+ Nothing -> bug "getForceReplace: unable to apply detected force replaces"+ Just tree'' -> do+ put tree''+ return patches+++-- | 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 rt p wR wU wT -> UpdateWorking+ -> FreeLeft (FL (PrimOf p)) -> IO ()+addPendingDiffToPending _ NoUpdateWorking _ = return ()+addPendingDiffToPending repo uw@YesUpdateWorking newP = do+ (toPend :> _) <-+ readPendingAndWorking (UseIndex, ScanKnown, MyersDiff)+ NoLookForMoves NoLookForReplaces repo Nothing+ invalidateIndex repo+ case unFreeLeft newP of+ (Sealed p) -> do recordedState <- readRecorded repo+ Pending.makeNewPending repo uw (toPend +>+ p) recordedState++-- | Add an 'FL' of patches starting from the working state to the pending patch,+-- including as much extra context as is necessary (context meaning+-- 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 rt p wR wU wT -> UpdateWorking+ -> FL (PrimOf p) wU wY -> IO ()+addToPending _ NoUpdateWorking _ = return ()+addToPending repo uw@YesUpdateWorking p = do+ (toPend :> toUnrec) <- readPendingAndWorking (UseIndex, ScanKnown, MyersDiff)+ NoLookForMoves NoLookForReplaces repo Nothing+ invalidateIndex repo+ case genCommuteWhatWeCanRL commuteFL (reverseFL toUnrec :> p) of+ (toP' :> p' :> _excessUnrec) -> do+ recordedState <- readRecorded repo+ Pending.makeNewPending repo uw+ (toPend +>+ reverseRL toP' +>+ p') recordedState
@@ -35,30 +35,24 @@ import Darcs.Util.Path ( AbsolutePath ) import Darcs.Util.Prompt ( askUser ) import Darcs.Repository.Prefs ( getPrefval )-import Darcs.Patch- ( RepoPatch ) -import Darcs.Repository.Internal- ( setScriptsExecutable- , withTentative- )+import Darcs.Repository.Hashed ( withTentative )+import Darcs.Repository.Working ( setScriptsExecutable ) import Darcs.Repository.Flags ( LeaveTestDir(..) , Verbosity(..) , SetScriptsExecutable(..) , RunTest (..)+ , HookConfig (..) ) import Darcs.Repository.InternalTypes- ( Repository(..) )+ ( Repository, repoLocation ) import Darcs.Util.Progress ( debugMessage ) import Darcs.Util.Lock ( withTempDir , withPermDir )-import Darcs.Patch.Apply ( ApplyState )-import Darcs.Util.Tree ( Tree ) - getTest :: Verbosity -> IO (IO ExitCode) getTest verb = let putInfo s = when (verb /= Quiet) $ putStr s@@ -71,8 +65,8 @@ putInfo "Running test...\n" runTest testcode putInfo -runPosthook :: Maybe String -> Bool -> Verbosity -> AbsolutePath -> IO ExitCode-runPosthook mPostHook askPostHook verb repodir+runPosthook :: HookConfig -> Verbosity -> AbsolutePath -> IO ExitCode+runPosthook (HookConfig mPostHook askPostHook) verb repodir = do ph <- getPosthook mPostHook askPostHook withCurrentDirectory repodir $ runHook verb "Posthook" ph @@ -91,8 +85,8 @@ _ -> putStrLn "Posthook cancelled..." >> return Nothing else return $ Just command -runPrehook :: Maybe String -> Bool -> Verbosity -> AbsolutePath -> IO ExitCode-runPrehook mPreHookCmd askPreHook verb repodir =+runPrehook :: HookConfig -> Verbosity -> AbsolutePath -> IO ExitCode+runPrehook (HookConfig mPreHookCmd askPreHook) verb repodir = do ph <- getPrehook mPreHookCmd askPreHook withCurrentDirectory repodir $ runHook verb "Prehook" ph @@ -121,8 +115,7 @@ else hPutStrLn stderr $ cname++" failed!" return ec -testTentative :: (RepoPatch p, ApplyState p ~ Tree)- => Repository rt p wR wU wT+testTentative :: Repository rt p wR wU wT -> RunTest -> LeaveTestDir -> SetScriptsExecutable@@ -139,8 +132,7 @@ return ec -testAny :: RepoPatch p- => (Repository rt p wR wU wT+testAny :: (Repository rt p wR wU wT -> ((AbsolutePath -> IO ExitCode) -> IO ExitCode) -> (AbsolutePath -> IO ExitCode) -> IO ExitCode )@@ -150,10 +142,12 @@ -> SetScriptsExecutable -> Verbosity -> IO ExitCode-testAny withD repository@(Repo dir _ _ _) doRunTest ltd sse verb =+testAny withD repository doRunTest ltd sse verb = debugMessage "Considering whether to test..." >>- if doRunTest == NoRunTest then return ExitSuccess else withCurrentDirectory dir $- do let putInfo = if verb == Quiet then const (return ()) else putStrLn+ if doRunTest == NoRunTest+ then return ExitSuccess+ else withCurrentDirectory (repoLocation repository) $ do+ let putInfo = if verb == Quiet then const (return ()) else putStrLn debugMessage "About to run test if it exists." testline <- getPrefval "test" case testline of
@@ -0,0 +1,75 @@+module Darcs.Repository.Working+ ( applyToWorking+ , setScriptsExecutable+ , setScriptsExecutablePatches+ ) where++import Control.Monad ( when, unless, filterM )+import System.Directory ( doesFileExist )++import qualified Data.ByteString as B ( readFile+ , isPrefixOf+ )+import qualified Data.ByteString.Char8 as BC (pack)++import Darcs.Util.File ( withCurrentDirectory )+import Darcs.Util.Progress ( debugMessage )+import Darcs.Util.Workaround ( setExecutable )+import Darcs.Util.Tree ( Tree )+import Darcs.Util.Path ( anchorPath )+import qualified Darcs.Util.Tree as Tree++import Darcs.Patch ( RepoPatch, apply, listTouchedFiles )+import Darcs.Patch.Apply ( ApplyState )+import Darcs.Patch.Prim ( PrimOf )+import Darcs.Patch.Witnesses.Ordered+ ( FL(..) )+import Darcs.Patch.Dummy ( DummyPatch )+import Darcs.Patch.Inspect ( PatchInspect )++import Darcs.Repository.Format ( RepoProperty( NoWorkingDir ), formatHas )+import Darcs.Repository.Flags ( Verbosity(..) )+import Darcs.Repository.InternalTypes+ ( Repository+ , repoFormat+ , repoLocation+ , coerceU )+import Darcs.Repository.ApplyPatches ( runTolerantly, runSilently )+import Darcs.Repository.State ( readWorking )++applyToWorking :: (ApplyState p ~ Tree, RepoPatch p)+ => Repository rt p wR wU wT -> Verbosity -> FL (PrimOf p) wU wY+ -> IO (Repository rt p wR wY wT)+applyToWorking repo verb patch =+ do+ unless (formatHas NoWorkingDir (repoFormat repo)) $+ withCurrentDirectory (repoLocation repo) $+ if verb == Quiet+ then runSilently $ apply patch+ else runTolerantly $ apply patch+ return $ coerceU repo++-- | Sets scripts in or below the current directory executable.+-- A script is any file that starts with the bytes '#!'.+-- This is used for --set-scripts-executable.+setScriptsExecutable_ :: PatchInspect p => Maybe (p wX wY) -> IO ()+setScriptsExecutable_ pw = do+ debugMessage "Making scripts executable"+ tree <- readWorking+ paths <- case pw of+ Just ps -> filterM doesFileExist $ listTouchedFiles ps+ Nothing -> return [ anchorPath "." p | (p, Tree.File _) <- Tree.list tree ]+ let setExecutableIfScript f =+ do contents <- B.readFile f+ when (BC.pack "#!" `B.isPrefixOf` contents) $ do+ debugMessage ("Making executable: " ++ f)+ setExecutable f True+ mapM_ setExecutableIfScript paths++setScriptsExecutable :: IO ()+setScriptsExecutable = setScriptsExecutable_ (Nothing :: Maybe (FL DummyPatch wX wY))++setScriptsExecutablePatches :: PatchInspect p => p wX wY -> IO ()+setScriptsExecutablePatches = setScriptsExecutable_ . Just++
@@ -11,22 +11,24 @@ import Control.Exception ( catch, fromException, SomeException, throwIO ) import Control.Monad ( when, unless )+import qualified Data.ByteString.Char8 as BC import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd ) import Darcs.Util.SignalHandler ( withSignalsBlocked ) import Darcs.UI.Commands ( putVerbose , putInfo- , printDryRunMessageAndExit , setEnvDarcsPatches )+import Darcs.UI.Commands.Util ( printDryRunMessageAndExit ) import Darcs.UI.CommandsAux ( checkPaths ) import Darcs.UI.Flags- ( DarcsFlag, verbosity, compression, reorder, allowConflicts, externalMerge+ ( DarcsFlag, verbosity, compress, reorder, allowConflicts, externalMerge , wantGuiPause, diffingOpts, setScriptsExecutable, isInteractive, testChanges- , hasXmlOutput, getReply, getCc, getSendmailCmd, hasSummary, dryRun+ , xmlOutput, reply, getCc, getSendmailCmd, dryRun ) import qualified Darcs.UI.Options.All as O+import Darcs.UI.Options ( (?) ) import Darcs.UI.Commands.Util ( testTentativeAndMaybeExit ) import Darcs.Repository.Flags ( UpdateWorking(..) ) import Darcs.Repository@@ -38,7 +40,7 @@ , setScriptsExecutablePatches ) import Darcs.Repository.Job ( RepoJob(RepoJob) )-import Darcs.Patch ( RepoPatch, RepoType, IsRepoType, description, PrimOf )+import Darcs.Patch ( RepoPatch, RepoType, IsRepoType, description ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Witnesses.Ordered ( FL, mapFL, nullFL )@@ -63,7 +65,7 @@ -> [DarcsFlag] -> (forall rt p wR wU . ( IsRepoType rt, ApplierRepoTypeConstraint pa rt- , RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree+ , RepoPatch p, ApplyState p ~ Tree ) => (PatchProxy p -> Repository rt p wR wU wR -> IO ())) -> RepoJob ()@@ -71,7 +73,7 @@ applyPatches :: forall rt p wR wU wT wX wZ . ( ApplierRepoTypeConstraint pa rt, IsRepoType rt- , RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree+ , RepoPatch p, ApplyState p ~ Tree ) => pa -> PatchProxy p@@ -91,18 +93,18 @@ standardApplyPatches :: forall rt p wR wU wT wX wZ- . (IsRepoType rt, RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree)+ . (IsRepoType rt, RepoPatch p, 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)- (hasSummary O.NoSummary opts)- (dryRun opts)- (hasXmlOutput opts)+ (verbosity ? opts)+ (O.summary ? opts)+ (dryRun ? opts)+ (xmlOutput ? opts) (isInteractive True opts) to_be_applied- when (nullFL to_be_applied && reorder opts == O.NoReorder) $ do + when (nullFL to_be_applied && reorder ? opts == O.NoReorder) $ do putStrLn $ "You don't want to " ++ cmdName ++ " any patches, so I'm exiting!" exitSuccess checkPaths opts to_be_applied@@ -113,30 +115,30 @@ setEnvDarcsPatches to_be_applied Sealed pw <- tentativelyMergePatches repository cmdName (allowConflicts opts) YesUpdateWorking- (externalMerge opts) (wantGuiPause opts)- (compression opts) (verbosity opts)- (reorder opts) (diffingOpts opts)+ (externalMerge ? opts) (wantGuiPause opts)+ (compress ? opts) (verbosity ? opts)+ (reorder ? opts) (diffingOpts opts) us' to_be_applied invalidateIndex repository testTentativeAndMaybeExit repository- (verbosity opts)- (testChanges opts)- (setScriptsExecutable opts)+ (verbosity ? opts)+ (testChanges ? opts)+ (setScriptsExecutable ? opts) (isInteractive True opts) "those patches do not pass the tests." (cmdName ++ " them") Nothing- withSignalsBlocked $ do finalizeRepositoryChanges repository YesUpdateWorking (compression opts)- _ <- applyToWorking repository (verbosity opts) pw `catch` \(e :: SomeException) ->+ withSignalsBlocked $ do finalizeRepositoryChanges repository YesUpdateWorking (compress ? opts)+ _ <- applyToWorking repository (verbosity ? opts) pw `catch` \(e :: SomeException) -> fail ("Error applying patch to working dir:\n" ++ show e)- when (setScriptsExecutable opts == O.YesSetScriptsExecutable) $+ when (setScriptsExecutable ? opts == O.YesSetScriptsExecutable) $ setScriptsExecutablePatches pw return ()- case (nullFL to_be_applied, reorder opts == O.Reorder) of+ case (nullFL to_be_applied, reorder ? opts == O.Reorder) of (True,True) -> putInfo opts $ text $ "Nothing to " ++ cmdName ++ ", finished reordering." (False,True) -> putInfo opts $ text $ "Finished " ++ cmdName ++ "ing and reordering." _ -> putInfo opts $ text $ "Finished " ++ cmdName ++ "ing." redirectOutput :: [DarcsFlag] -> String -> IO () -> IO ()-redirectOutput opts to doit = case getReply opts of+redirectOutput opts to doit = case reply ? opts of Nothing -> doit Just from -> withStdoutTemp $ \tempf -> doitAndCleanup `catch` sendit tempf from where@@ -171,7 +173,7 @@ -- an email. sanitizeFile :: FilePath -> IO String-sanitizeFile f = sanitize `fmap` readBinFile f+sanitizeFile f = sanitize . BC.unpack <$> readBinFile f where sanitize s = wash $ remove_backspaces "" s wash ('\000':s) = "\\NUL" ++ wash s wash ('\026':s) = "\\EOF" ++ wash s
@@ -15,12 +15,13 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} module Darcs.UI.Commands ( CommandControl ( CommandData, HiddenCommand, GroupName ) , DarcsCommand ( .. ) , WrappedCommand(..) , wrappedCommandName+ , wrappedCommandDescription , commandAlias , commandStub , commandOptions@@ -28,12 +29,7 @@ , withStdOpts , disambiguateCommands , CommandArgs(..)- , getCommandHelp- , getCommandMiniHelp , getSubcommands- , usage- , usageHelper- , subusage , extractCommands , extractAllCommands , normalCommand@@ -46,7 +42,6 @@ , putWarning , putVerboseWarning , abortRun- , printDryRunMessageAndExit , setEnvDarcsPatches , setEnvDarcsFiles , defaultRepo@@ -62,50 +57,41 @@ import Prelude hiding ( (^) ) import Control.Monad ( when, unless ) import Data.List ( sort, isPrefixOf )-import Data.Maybe ( catMaybes ) import Darcs.Util.Tree ( Tree ) import System.Console.GetOpt ( OptDescr )-import System.Exit ( exitSuccess ) import System.IO ( stderr )-#ifndef WIN32-import System.Posix.Env ( setEnv )-+import System.IO.Error ( catchIOError )+import System.Environment ( setEnv ) import Darcs.Patch ( listTouchedFiles ) import qualified Darcs.Patch ( summary )-#endif-import Darcs.Patch ( RepoPatch, xmlSummary, Patchy )+import Darcs.Patch ( RepoPatch ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Info ( toXml ) import Darcs.Patch.Inspect ( PatchInspect )-import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info, hopefullyM )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info ) import Darcs.Patch.Witnesses.Ordered ( FL, mapFL ) import qualified Darcs.Repository as R ( amInHashedRepository, amInRepository , amNotInRepository, findRepository ) import Darcs.Repository.Prefs ( defaultrepo ) -import Darcs.UI.Options ( DarcsOption, DarcsOptDescr, (^), optDescr, odesc, parseFlags )+import Darcs.UI.Options ( DarcsOption, DarcsOptDescr, (^), optDescr, odesc, parseFlags, (?) ) import Darcs.UI.Options.All- ( StdCmdAction, stdCmdActions, anyVerbosity, UseCache, useCache, hooks- , Verbosity(..), verbosity, Summary(..), DryRun(..), dryRun, XmlOutput(..) )+ ( StdCmdAction, stdCmdActions, anyVerbosity, UseCache, useCache, HooksConfig, hooks+ , Verbosity(..), DryRun(..), dryRun+ ) -import Darcs.UI.Flags ( remoteRepos, workRepo, DarcsFlag )-import Darcs.UI.PrintPatch ( showFriendly )-import Darcs.UI.Usage ( usageInfo )+import Darcs.UI.Flags ( DarcsFlag, remoteRepos, workRepo, quiet, verbose ) +import Darcs.Util.ByteString ( decodeLocale, packStringToUTF8 ) import Darcs.Util.Path ( AbsolutePath )-import Darcs.Util.Printer ( Doc, putDocLn, hPutDocLn, text, (<+>), errorDoc- , vsep, insertBeforeLastline, prefix, ($$), vcat-#ifndef WIN32- , renderString-#endif- , RenderMode(..)- )-#ifndef WIN32-import Darcs.Util.Progress ( beginTedious, endTedious, tediousSize- , finishedOneIO )-#endif-import Darcs.Util.Text ( chompTrailingNewline )+import Darcs.Util.Printer+ ( Doc, text, (<+>), ($$), vcat+ , putDocLnWith, hPutDocLn, errorDoc, renderString+ )+import Darcs.Util.Printer.Color ( fancyPrinters )+import Darcs.Util.Progress+ ( debugMessage, beginTedious, endTedious, tediousSize, finishedOneIO ) extractCommands :: [CommandControl] -> [WrappedCommand] extractCommands ccl = [ cmd | CommandData cmd <- ccl ]@@ -158,7 +144,8 @@ (AbsolutePath, AbsolutePath) -> parsedFlags -> [String] -> IO () , commandPrereq :: [DarcsFlag] -> IO (Either String ())- , commandGetArgPossibilities :: IO [String]+ , commandCompleteArgs :: (AbsolutePath, AbsolutePath)+ -> [DarcsFlag] -> [String] -> IO [String] , commandArgdefaults :: [DarcsFlag] -> AbsolutePath -> [String] -> IO [String] , commandBasicOptions :: [DarcsOptDescr DarcsFlag]@@ -177,7 +164,7 @@ } withStdOpts :: DarcsOption (Maybe StdCmdAction -> Bool -> Bool -> Verbosity -> Bool -> b) c- -> DarcsOption (UseCache -> Maybe String -> Bool -> Maybe String -> Bool -> a) b+ -> DarcsOption (UseCache -> HooksConfig -> a) b -> DarcsOption a c withStdOpts basicOpts advancedOpts = basicOpts ^ stdCmdActions ^ anyVerbosity ^ advancedOpts ^ useCache ^ hooks@@ -199,7 +186,7 @@ nodefaults _ _ = return getSubcommands :: DarcsCommand pf -> [CommandControl]-getSubcommands c@(SuperCommand {}) = commandSubCommands c+getSubcommands c@(SuperCommand {}) = commandGroup "Subcommands:" : commandSubCommands c getSubcommands _ = [] commandAlias :: String -> Maybe (DarcsCommand pf) -> DarcsCommand pf -> DarcsCommand pf@@ -222,120 +209,10 @@ , commandCommand = \_ _ _ -> putStr h } -usage :: [CommandControl] -> String-usage cs = unlines- [ "Usage: darcs COMMAND ..."- , ""- , "Commands:"- , usageHelper cs- , "Use 'darcs COMMAND --help' for help on a single command."- , "Use 'darcs --version' to see the darcs version number."- , "Use 'darcs --exact-version' to see a detailed darcs version."- , "Use 'darcs help patterns' for help on patch matching."- , "Use 'darcs help environment' for help on environment variables."- , "Use 'darcs help manpage' to display help in the manpage format."- , "Use 'darcs help markdown' to display help in the markdown format."- , ""- , "Check bug reports at http://bugs.darcs.net/"- ]--subusage :: DarcsCommand pf -> String-subusage super = usageInfo header (odesc stdCmdActions) ++ superHelp- where- header = unlines [ unwords [ "Usage:"- , commandProgramName super- , commandName super- , "SUBCOMMAND ..."- ]- , ""- , commandDescription super- , ""- , "Subcommands:"- , usageHelper (getSubcommands super)- , "Options:"- ]- superHelp = '\n' : commandHelp super--usageHelper :: [CommandControl] -> String-usageHelper xs = usageHelper' (maximum $ 15 : (catMaybes $ map f xs)) xs- where- -- returns length of necessary tabbing this command- f (CommandData c) = Just ((+2) . length . wrappedCommandName $ c)- f _ = Nothing--usageHelper' :: Int -> [CommandControl] -> String-usageHelper' _ [] = ""-usageHelper' x (HiddenCommand _ : cs) = usageHelper' x cs-usageHelper' x (CommandData c : cs) = " " ++ padSpaces (wrappedCommandName c) x- ++ chompTrailingNewline (wrappedCommandDescription c)- ++ "\n" ++ usageHelper' x cs-usageHelper' x (GroupName n : cs) = "\n" ++ n ++ "\n" ++ usageHelper' x cs--padSpaces :: String -> Int -> String-padSpaces s n = s ++ replicate (n - length s) ' '- superName :: Maybe (DarcsCommand pf) -> String superName Nothing = "" superName (Just x) = commandName x ++ " " -getCommandMiniHelp :: Maybe (DarcsCommand pf1) -> DarcsCommand pf2 -> String-getCommandMiniHelp msuper cmd = unlines- [ getCommandHelpCore msuper cmd- , ""- , unwords [ "See"- , commandProgramName cmd- , "help"- , maybe "" ((++ " ") . commandName) msuper ++ commandName cmd- , "for details."- ]- ]--getCommandHelp :: Maybe (DarcsCommand pf1) -> DarcsCommand pf2 -> String-getCommandHelp msuper cmd = basicHelp ++ advancedHelp ++ cmdHelp- where- basicHelp = unlines (reverse basicR)-- advancedHelp = if null advanced- then ""- else '\n' :- unlines ("Advanced options:" : reverse advancedR)-- cmdHelp = '\n' : commandHelp cmd-- -- we could just call usageInfo twice, but then the advanced- -- options might not line up with the basic ones (no short switches)- (advancedR, basicR) = splitAt (length advanced) . reverse . lines $- combinedUsage-- combinedUsage = let header = getCommandHelpCore msuper cmd ++ subcommands- ++ "\n\nOptions:"- in usageInfo header (basic ++ advanced)-- (basic, advanced) = commandAlloptions cmd-- subcommands = case msuper of- Nothing -> case getSubcommands cmd of- [] -> []- s -> "\n\nSubcommands:\n"- ++ usageHelper s- -- we don't want to list subcommands if we're already- -- specifying them- Just _ -> ""--getCommandHelpCore :: Maybe (DarcsCommand pf1) -> DarcsCommand pf2 -> String-getCommandHelpCore msuper cmd =- unwords [ "Usage:"- , commandProgramName cmd- , superName msuper ++ commandName cmd- , "[OPTION]..."- , unwords args_help- ]- ++ "\n" ++ commandDescription cmd- where- args_help = case cmd of- (DarcsCommand {}) -> commandExtraArgHelp cmd- _ -> []- data CommandArgs where CommandOnly :: DarcsCommand parsedFlags -> CommandArgs SuperCommandOnly :: DarcsCommand parsedFlags -> CommandArgs@@ -366,89 +243,36 @@ potentials = [c | c <- extractCommands cs, cmd `isPrefixOf` wrappedCommandName c] ++ [h | h <- extractHiddenCommands cs, cmd == wrappedCommandName h] -amVerbose :: [DarcsFlag] -> Bool-amVerbose = (== Verbose) . parseFlags verbosity--amQuiet :: [DarcsFlag] -> Bool-amQuiet = (== Quiet) . parseFlags verbosity- putVerbose :: [DarcsFlag] -> Doc -> IO ()-putVerbose flags = when (amVerbose flags) . putDocLn+putVerbose flags = when (verbose flags) . putDocLnWith fancyPrinters putInfo :: [DarcsFlag] -> Doc -> IO ()-putInfo flags = unless (amQuiet flags) . putDocLn+putInfo flags = unless (quiet flags) . putDocLnWith fancyPrinters putWarning :: [DarcsFlag] -> Doc -> IO ()-putWarning flags = unless (amQuiet flags) . hPutDocLn Encode stderr+putWarning flags = unless (quiet flags) . hPutDocLn stderr putVerboseWarning :: [DarcsFlag] -> Doc -> IO ()-putVerboseWarning flags = when (amVerbose flags) . hPutDocLn Encode stderr+putVerboseWarning flags = when (verbose flags) . hPutDocLn stderr abortRun :: [DarcsFlag] -> Doc -> IO () abortRun flags msg = if parseFlags dryRun flags == YesDryRun- then putInfo flags $ text "NOTE:" <+> msg+ then putInfo flags $ "NOTE:" <+> msg else errorDoc msg --- | @'printDryRunMessageAndExit' action flags patches@ prints a string--- representing the action that would be taken if the @--dry-run@ option had--- not been passed to darcs. Then darcs exits successfully. @action@ is the--- name of the action being taken, like @\"push\"@ @flags@ is the list of flags--- which were sent to darcs @patches@ is the sequence of patches which would be--- touched by @action@.-printDryRunMessageAndExit :: (RepoPatch p, ApplyState p ~ Tree)- => String- -> Verbosity -> Summary -> DryRun -> XmlOutput- -> Bool -- interactive- -> FL (PatchInfoAnd rt p) wX wY- -> IO ()-printDryRunMessageAndExit action v s d x interactive patches = do- when (d == YesDryRun) $ do- putInfoX . text $ unwords [ "Would"- , action- , "the following changes:"- ]- putDocLn put_mode- putInfoX $ text ""- putInfoX $ text "Making no changes: this is a dry run."- exitSuccess- when (not interactive && s == YesSummary) $ do- putInfoX . text $ unwords [ "Will"- , action- , "the following changes:"- ]- putDocLn put_mode- where- put_mode = if x == YesXml- then text "<patches>" $$- vcat (mapFL (indent . xml_info s) patches) $$- text "</patches>"- else vsep $ mapFL (showFriendly v s) patches-- putInfoX = if x == YesXml then const (return ()) else putDocLn-- xml_info YesSummary = xml_with_summary- xml_info NoSummary = toXml . info-- xml_with_summary hp- | Just p <- hopefullyM hp = insertBeforeLastline (toXml $ info hp)- (indent $ xmlSummary p)- xml_with_summary hp = toXml (info hp)-- indent = prefix " "- -- | 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 rt p) wX wY -> IO ()-#ifndef WIN32 setEnvDarcsPatches ps = do let k = "Defining set of chosen patches"+ debugMessage $ unlines ("setEnvDarcsPatches:" : listTouchedFiles ps) beginTedious k tediousSize k 3 finishedOneIO k "DARCS_PATCHES"- setEnvCautiously "DARCS_PATCHES" (renderString Encode $ Darcs.Patch.summary ps)+ setEnvCautiously "DARCS_PATCHES" (renderString $ Darcs.Patch.summary ps) finishedOneIO k "DARCS_PATCHES_XML"- setEnvCautiously "DARCS_PATCHES_XML" . renderString Encode $+ setEnvCautiously "DARCS_PATCHES_XML" . renderString $ text "<patches>" $$ vcat (mapFL (toXml . info) ps) $$ text "</patches>"@@ -456,43 +280,37 @@ setEnvCautiously "DARCS_FILES" $ unlines (listTouchedFiles ps) endTedious k +-- | Set the DARCS_FILES environment variable to the files touched by the+-- given patch, one per line, for use in post-hooks.+setEnvDarcsFiles :: (PatchInspect p) => p wX wY -> IO ()+setEnvDarcsFiles ps =+ setEnvCautiously "DARCS_FILES" $ unlines (listTouchedFiles ps)+ -- | Set some environment variable to the given value, unless said value is -- longer than 10K characters, in which case do nothing. setEnvCautiously :: String -> String -> IO () setEnvCautiously e v | toobig (10 * 1024) v = return ()- | otherwise = setEnv e v True+ | otherwise =+ setEnv e v `catchIOError` (\_ -> setEnv e (decodeLocale (packStringToUTF8 v))) where -- note: not using (length v) because we want to be more lazy than that toobig :: Int -> [a] -> Bool toobig 0 _ = True toobig _ [] = False toobig n (_ : xs) = toobig (n - 1) xs-#else-setEnvDarcsPatches _ = return ()-#endif --- | Set the DARCS_FILES environment variable to the files touched by the--- given patch, one per line, for use in post-hooks.-setEnvDarcsFiles :: (PatchInspect p, Patchy p) => p wX wY -> IO ()-#ifndef WIN32-setEnvDarcsFiles ps =- setEnvCautiously "DARCS_FILES" $ unlines (listTouchedFiles ps)-#else-setEnvDarcsFiles _ = return ()-#endif- defaultRepo :: [DarcsFlag] -> AbsolutePath -> [String] -> IO [String]-defaultRepo fs = defaultrepo (remoteRepos fs)+defaultRepo fs = defaultrepo (remoteRepos ? fs) amInHashedRepository :: [DarcsFlag] -> IO (Either String ())-amInHashedRepository fs = R.amInHashedRepository (workRepo fs)+amInHashedRepository fs = R.amInHashedRepository (workRepo ? fs) amInRepository :: [DarcsFlag] -> IO (Either String ())-amInRepository fs = R.amInRepository (workRepo fs)+amInRepository fs = R.amInRepository (workRepo ? fs) amNotInRepository :: [DarcsFlag] -> IO (Either String ())-amNotInRepository fs = R.amNotInRepository (workRepo fs)+amNotInRepository fs = R.amNotInRepository (workRepo ? fs) findRepository :: [DarcsFlag] -> IO (Either String ())-findRepository fs = R.findRepository (workRepo fs)+findRepository fs = R.findRepository (workRepo ? fs)
@@ -23,30 +23,23 @@ -- Stability : experimental -- Portability : portable -{-# LANGUAGE CPP #-}-module Darcs.UI.Commands.Add- (- add- , expandDirs- ) where+module Darcs.UI.Commands.Add ( add ) where import Prelude () import Darcs.Prelude -#include "impossible.h"--import Prelude hiding ( (^) )- import Control.Exception ( catch, IOException )-import Control.Monad ( when, unless, liftM )+import Control.Monad ( when, unless ) import Data.List ( (\\), nub ) import Data.List.Ordered ( nubSort ) import Data.Maybe ( isNothing, maybeToList ) import Darcs.Util.Printer ( text ) import Darcs.Util.Tree ( Tree, findTree, expand )-import Darcs.Util.Path ( floatPath, anchorPath, parents,- SubPath, toFilePath, simpleSubPath, toPath, AbsolutePath )-import System.FilePath.Posix ( takeDirectory, (</>) )+import Darcs.Util.Path+ ( floatPath, anchorPath, parents+ , SubPath, toFilePath, AbsolutePath+ )+import System.FilePath.Posix ( takeDirectory ) import System.Posix.Files ( isRegularFile, isDirectory, isSymbolicLink ) import System.Directory ( getPermissions, readable ) @@ -55,32 +48,30 @@ import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, putInfo, putWarning, putVerboseWarning , nodefaults, amInHashedRepository)+import Darcs.UI.Commands.Util.Tree ( treeHas, treeHasDir, treeHasAnycase )+import Darcs.UI.Commands.Util ( expandDirs, doesDirectoryReallyExist )+import Darcs.UI.Completion ( unknownFileArgs ) import Darcs.UI.Flags ( DarcsFlag- , includeBoring, doAllowCaseOnly, doAllowWindowsReserved, useCache, dryRun, umask- , fixSubPaths, verbosity )+ , includeBoring, allowCaseDifferingFilenames, allowWindowsReservedFilenames, useCache, dryRun, umask+ , fixSubPaths, quiet ) import Darcs.UI.Options- ( DarcsOption- , (^), odesc, ocheck, onormalise, defaultFlags, parseFlags )+ ( (^), odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O-import Darcs.UI.Commands.Util.Tree ( treeHas, treeHasDir, treeHasAnycase ) import Darcs.Repository.Flags ( UpdateWorking(..) )-import Darcs.Patch ( Patchy, PrimPatch, applyToTree, addfile, adddir )+import Darcs.Patch ( PrimPatch, applyToTree, addfile, adddir ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Repository.State ( readRecordedAndPending, updateIndex ) import Darcs.Repository ( withRepoLock , RepoJob(..) , addToPending- , listFiles- , listUnregisteredFiles ) import Darcs.Repository.Prefs ( darcsdirFilter, boringFileFilter )-import Darcs.Util.File ( getFileStatus, withCurrentDirectory )+import Darcs.Util.File ( getFileStatus ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (+>+), nullFL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), Gap(..), FreeLeft, unFreeLeft ) - addDescription :: String addDescription = "Add new files to version control." @@ -111,38 +102,6 @@ "`ReadMe` and `README`). If `--case-ok` is used, the repository might be\n" ++ "unusable on those systems!\n\n" -addBasicOpts :: DarcsOption a- (O.IncludeBoring -> Bool -> Bool -> Bool -> Maybe String -> O.DryRun -> a)-addBasicOpts = O.includeBoring- ^ O.allowProblematicFilenames- ^ O.recursive- ^ O.workingRepoDir- ^ O.dryRun--addAdvancedOpts :: DarcsOption a (O.UMask -> a)-addAdvancedOpts = O.umask--addOpts :: DarcsOption a- (O.IncludeBoring- -> Bool- -> Bool- -> Bool- -> Maybe String- -> O.DryRun- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-addOpts = withStdOpts addBasicOpts addAdvancedOpts- add :: DarcsCommand [DarcsFlag] add = DarcsCommand { commandProgramName = "darcs"@@ -153,8 +112,7 @@ , commandExtraArgHelp = [ "<FILE or DIRECTORY> ..." ] , commandCommand = addCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = listUnregisteredFiles False- -- bash completion should not offer boring files+ , commandCompleteArgs = unknownFileArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc addAdvancedOpts , commandBasicOptions = odesc addBasicOpts@@ -162,6 +120,15 @@ , commandCheckOptions = ocheck addOpts , commandParseOptions = onormalise addOpts }+ where+ addBasicOpts+ = O.includeBoring+ ^ O.allowProblematicFilenames+ ^ O.recursive+ ^ O.repoDir+ ^ O.dryRun+ addAdvancedOpts = O.umask+ addOpts = withStdOpts addBasicOpts addAdvancedOpts addCmd :: (AbsolutePath, AbsolutePath)@@ -181,7 +148,9 @@ addFiles :: [DarcsFlag] -- ^ Command options -> [SubPath] -> IO ()-addFiles opts origfiles = withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+addFiles opts origfiles =+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repository -> do -- TODO do not expand here, and use findM/findIO or such later -- (needs adding to hashed-storage first though) cur <- expand =<< readRecordedAndPending repository@@ -196,19 +165,19 @@ mapM_ (putWarning opts . text . ((msgSkipping msgs ++ " boring file ")++)) $ flist \\ nboring flist Sealed ps <- fmap unFreeLeft $ addp msgs opts cur $ nboring flist- when (nullFL ps && not (null origfiles) && notQuiet) $+ -- TODO whether we fail or not depends on verbosity BAD BAD BAD+ when (nullFL ps && not (null origfiles) && not (quiet opts)) $ fail "No files were added" unless gotDryRun $ do addToPending repository YesUpdateWorking ps updateIndex repository where- gotDryRun = dryRun opts == O.YesDryRun+ gotDryRun = dryRun ? opts == O.YesDryRun msgs | gotDryRun = dryRunMessages | otherwise = normalMessages- notQuiet = verbosity opts /= O.Quiet -addp :: forall prim . (Patchy prim, PrimPatch prim, ApplyState prim ~ Tree)+addp :: forall prim . (PrimPatch prim, ApplyState prim ~ Tree) => AddMessages -> [DarcsFlag] -> Tree IO@@ -319,8 +288,8 @@ msgSkipping msgs ++ " '" ++ f ++ "' ... " ++ show e return (cur, Nothing, Nothing) parentdir = takeDirectory f- gotAllowCaseOnly = doAllowCaseOnly opts- gotAllowWindowsReserved = doAllowWindowsReserved opts+ gotAllowCaseOnly = allowCaseDifferingFilenames ? opts+ gotAllowWindowsReserved = allowWindowsReservedFilenames ? opts data AddMessages = AddMessages@@ -352,34 +321,9 @@ } -doesDirectoryReallyExist :: FilePath -> IO Bool-doesDirectoryReallyExist f = maybe False isDirectory `fmap` getFileStatus f---expandDirs :: Bool -> [SubPath]- -> IO [SubPath]-expandDirs doIncludeBoring fs =- do- liftM (map (fromJust . simpleSubPath)) $- concat `fmap` mapM (expandOne doIncludeBoring . toPath) fs---expandOne :: Bool -> FilePath- -> IO [FilePath]-expandOne doIncludeBoring "" = listFiles doIncludeBoring-expandOne doIncludeBoring f = do- isdir <- doesDirectoryReallyExist f- if not isdir- then return [f]- else do- fs <- withCurrentDirectory f (listFiles doIncludeBoring)- return $ f: map (f </>) fs-- getParents :: Tree IO -> [FilePath] -> [FilePath] getParents cur = map (anchorPath "") . go . map floatPath where go fs = filter (isNothing . findTree cur) $ concatMap parents fs-
@@ -43,8 +43,9 @@ , amInHashedRepository ) import Darcs.UI.Commands.Util ( announceFiles, testTentativeAndMaybeExit )+import Darcs.UI.Completion ( modifiedFileArgs, knownFileArgs ) import Darcs.UI.Flags ( DarcsFlag, diffOpts, fixSubPaths )-import Darcs.UI.Options ( DarcsOption, (^), oparse, odesc, ocheck, defaultFlags )+import Darcs.UI.Options ( DarcsOption, (^), oparse, odesc, ocheck, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.UI.PatchHeader ( updatePatchHeader, AskAboutDeps(..) , HijackOptions(..)@@ -70,12 +71,10 @@ , withManualRebaseUpdate , finalizeRepositoryChanges , invalidateIndex- , unrecordedChangesWithPatches+ , unrecordedChanges , readRecorded- , listRegisteredFiles ) import Darcs.Repository.Prefs ( globalPrefsDirDoc )-import Darcs.Repository.State ( getMovesPs, getReplaces ) import Darcs.UI.SelectChanges ( WhichChanges(..) , selectionContextPrim@@ -88,12 +87,10 @@ import Darcs.Util.Exception ( clarifyErrors ) 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 Darcs.Util.Tree( Tree )-import Darcs.Repository.Internal ( tentativelyRemoveFromPending )+import Darcs.Repository.Pending ( tentativelyRemoveFromPending ) amendDescription :: String@@ -151,11 +148,11 @@ ^ O.author ^ O.selectAuthor ^ O.patchname- ^ O.askdeps+ ^ O.askDeps ^ O.askLongComment ^ O.keepDate ^ O.lookfor- ^ O.workingRepoDir+ ^ O.repoDir ^ O.withContext ^ O.diffAlgorithm @@ -192,10 +189,7 @@ -> O.UMask -> O.SetScriptsExecutable -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool+ -> O.HooksConfig -> a) amendOpts = withStdOpts amendBasicOpts amendAdvancedOpts @@ -237,7 +231,7 @@ , commandExtraArgHelp = ["[FILE or DIRECTORY]..."] , commandCommand = amendCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = listRegisteredFiles+ , commandCompleteArgs = amendFileArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc amendAdvancedOpts , commandBasicOptions = odesc amendBasicOpts@@ -245,7 +239,11 @@ , commandCheckOptions = ocheck amendOpts , commandParseOptions = amendConfig }-+ where+ amendFileArgs fps flags args =+ if (O.amendUnrecord ? flags)+ then knownFileArgs fps flags args+ else modifiedFileArgs fps flags args amendrecord :: DarcsCommand AmendConfig amendrecord = commandAlias "amend-record" Nothing amend@@ -293,15 +291,9 @@ (_ :> 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- 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- go =<< unrecordedChangesWithPatches- movesPs (unsafeCoerceP replacePs :: FL (PrimOf p) wR wR)- (diffingOpts cfg) repository files+ else go =<< unrecordedChanges (diffingOpts cfg)+ (O.moves (lookfor cfg)) (O.replaces (lookfor 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
@@ -14,22 +14,21 @@ -- 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, OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings #-} module Darcs.UI.Commands.Annotate ( annotate ) where import Prelude () import Darcs.Prelude -import Prelude hiding ( (^) )- import Control.Arrow ( first )-import Control.Monad ( unless )+import Control.Monad ( when ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )-import Darcs.UI.Flags ( DarcsFlag(NoPatchIndexFlag), useCache, fixSubPaths, umask )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise- , defaultFlags, parseFlags )+import Darcs.UI.Completion ( knownFileArgs )+import Darcs.UI.Flags ( DarcsFlag, useCache, fixSubPaths, patchIndexYes )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise+ , defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.State ( readRecorded ) import Darcs.Repository@@ -38,11 +37,9 @@ , RepoJob(..) , readRepo , repoPatchType- , listRegisteredFiles )-import Darcs.Repository.Flags ( UpdateWorking(..) ) import Darcs.Repository.PatchIndex ( attemptCreatePatchIndex )-import Darcs.Patch.Set ( newset2RL )+import Darcs.Patch.Set ( patchSet2RL ) import Darcs.Patch ( invertRL ) import qualified Data.ByteString.Char8 as BC ( pack, concat, intercalate ) import Data.ByteString.Lazy ( toChunks )@@ -57,10 +54,8 @@ 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) )--#include "impossible.h"+ , AbsolutePath, SubPath )+import Darcs.Util.Exception ( die ) annotateDescription :: String annotateDescription = "Annotate lines of a file with the last patch that modified it."@@ -74,36 +69,6 @@ , "machine postprocessing." ] -annotateBasicOpts :: DarcsOption a- (Bool- -> [O.MatchFlag]- -> Maybe String- -> a)-annotateBasicOpts = O.machineReadable- ^ O.matchUpToOne- ^ O.workingRepoDir--annotateAdvancedOpts :: DarcsOption a (O.WithPatchIndex -> a)-annotateAdvancedOpts = O.patchIndexYes--annotateOpts :: DarcsOption a- ( Bool- -> [O.MatchFlag]- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.WithPatchIndex- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-annotateOpts = annotateBasicOpts `withStdOpts` annotateAdvancedOpts- annotate :: DarcsCommand [DarcsFlag] annotate = DarcsCommand { commandProgramName = "darcs"@@ -114,60 +79,69 @@ , commandExtraArgHelp = ["[FILE or DIRECTORY]"] , commandCommand = annotateCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = listRegisteredFiles+ , commandCompleteArgs = knownFileArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc annotateAdvancedOpts , commandBasicOptions = odesc annotateBasicOpts , commandDefaults = defaultFlags annotateOpts , commandCheckOptions = ocheck annotateOpts , commandParseOptions = onormalise annotateOpts-}+ }+ where+ annotateBasicOpts = O.machineReadable ^ O.matchUpToOne ^ O.repoDir+ annotateAdvancedOpts = O.patchIndexYes+ annotateOpts = annotateBasicOpts `withStdOpts` annotateAdvancedOpts annotateCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-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+ fixed_paths <- fixSubPaths fps args+ case fixed_paths of+ [] -> die "Error: annotate needs a filename to work with"+ (fixed_path:_) -> do+ when (patchIndexYes ? opts == O.YesPatchIndex)+ $ withRepoLockCanFail (useCache ? opts)+ $ RepoJob (\repo -> readRepo repo >>= attemptCreatePatchIndex repo)+ annotateCmd' opts fixed_path++annotateCmd' :: [DarcsFlag] -> SubPath -> IO ()+annotateCmd' opts fixed_path = withRepository (useCache ? opts) $ RepoJob $ \repository -> do+ let matchFlags = parseFlags O.matchUpToOne opts r <- readRepo repository- (origpath:_) <- fixSubPaths fps args recorded <- readRecorded repository- (patches, initial, path') <- if haveNonrangeMatch (repoPatchType repository) matchFlags then do Sealed x <- getOnePatchset repository matchFlags- let fn = [fp2fn $ toFilePath origpath]+ let fn = [fp2fn $ toFilePath fixed_path] nonRangeMatch = getNonrangeMatchS matchFlags r (_, [path], _) = withFileNames Nothing fn nonRangeMatch initial <- snd `fmap` virtualTreeIO (getNonrangeMatchS matchFlags r) recorded- return (seal $ newset2RL x, initial, toFilePath path)- else return (seal $ newset2RL r, recorded, toFilePath origpath)+ return (seal $ patchSet2RL x, initial, toFilePath path)+ else return (seal $ patchSet2RL r, recorded, toFilePath fixed_path) let path = "./" ++ path' found <- findM initial (floatPath $ toFilePath path) -- TODO need to decide about the --machine flag let fmt = if parseFlags O.machineReadable opts then A.machineFormat else A.format+ usePatchIndex <- (O.yes (O.patchIndexYes ? opts) &&) <$> canUsePatchIndex repository case found of- Nothing -> fail $ "No such file or directory: " ++ toFilePath path+ Nothing -> die $ "Error: no such file or directory: " ++ toFilePath path Just (SubTree s) -> do s' <- expand s let subs = map (fp2fn . (path </>) . anchorPath "" . fst) $ list s' showPath (n, File _) = BC.pack (path </> n) showPath (n, _) = BC.concat [BC.pack (path </> n), "/"] (Sealed ans_patches) <- do- upi <- canUsePatchIndex repository- if not upi+ if not usePatchIndex then return patches- else getRelevantSubsequence patches repository subs+ else getRelevantSubsequence patches repository r subs putStrLn $ fmt (BC.intercalate "\n" $ map (showPath . first (anchorPath "")) $ list s') $- A.annotateDirectory D.MyersDiff (invertRL ans_patches) (fp2fn path) subs+ A.annotateDirectory (invertRL ans_patches) (fp2fn path) subs Just (File b) -> do (Sealed ans_patches) <- do- upi <- canUsePatchIndex repository- if not upi+ if not usePatchIndex then return patches- else getRelevantSubsequence patches repository [fp2fn path]+ else getRelevantSubsequence patches repository r [fp2fn path] con <- BC.concat `fmap` toChunks `fmap` readBlob b- putStrLn $ fmt con $ A.annotate D.MyersDiff (invertRL ans_patches) (fp2fn path) con+ putStrLn $ fmt con $+ A.annotateFile (invertRL ans_patches) (fp2fn path) con Just (Stub _ _) -> impossible
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.UI.Commands.Apply ( apply, applyCmd , getPatchBundle -- used by darcsden@@ -26,7 +24,6 @@ import Darcs.Prelude import System.Exit ( exitSuccess )-import Prelude hiding ( (^) ) import Control.Monad ( when ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, hopefullyM, info )@@ -35,14 +32,15 @@ , putVerbose , amInHashedRepository )+import Darcs.UI.Completion ( fileArgs ) import Darcs.UI.Flags ( DarcsFlag- , doHappyForwarding, doReverse, verbosity, useCache, dryRun+ , happyForwarding, changesReverse, verbosity, useCache, dryRun , reorder, umask , fixUrl, getCc, getSendmailCmd- , isUnified, getReply+ , withContext, reply )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.Flags ( UpdateWorking(..) ) import Darcs.Util.Path ( toFilePath, AbsolutePath )@@ -53,10 +51,10 @@ , readRepo , filterOutConflicts )-import Darcs.Patch.Set ( Origin, newset2RL )-import Darcs.Patch ( IsRepoType, RepoPatch, PrimOf )+import Darcs.Patch.Set ( Origin, patchSet2RL )+import Darcs.Patch ( IsRepoType, RepoPatch ) import Darcs.Patch.Apply( ApplyState )-import Darcs.Patch.Info ( PatchInfo, showPatchInfoUI )+import Darcs.Patch.Info ( PatchInfo, displayPatchInfo ) import Darcs.Patch.Witnesses.Ordered ( RL(..), (:\/:)(..), (:>)(..) , mapRL, nullFL, reverseFL )@@ -85,13 +83,10 @@ import Darcs.Patch.Bundle ( scanBundle ) import Darcs.Util.Printer ( packedString, vcat, text, empty- , renderString, RenderMode(..)+ , renderString ) import Darcs.Util.Tree( Tree ) -#include "impossible.h"-- applyDescription :: String applyDescription = "Apply a patch bundle created by `darcs send'." @@ -138,99 +133,6 @@ stdindefault _ [] = return ["-"] stdindefault _ x = return x -conflictsOpt :: DarcsOption a (Maybe O.AllowConflicts -> a)-conflictsOpt = O.conflicts O.NoAllowConflicts--applyBasicOpts :: DarcsOption a- (O.Verify- -> O.Reorder- -> Maybe Bool- -> O.DryRun- -> O.XmlOutput- -> [O.MatchFlag]- -> Maybe O.AllowConflicts- -> O.ExternalMerge- -> O.RunTest- -> O.LeaveTestDir- -> Maybe String- -> O.DiffAlgorithm- -> a)-applyBasicOpts- = O.verify- ^ O.reorder- ^ O.interactive- ^ O.dryRunXml- ^ O.matchSeveral- ^ conflictsOpt- ^ O.useExternalMerge- ^ O.test- ^ O.leaveTestDir- ^ O.workingRepoDir- ^ O.diffAlgorithm--applyAdvancedOpts :: DarcsOption a- (Maybe String- -> Maybe String- -> Bool- -> (Bool, Maybe String)- -> O.UseIndex- -> O.Compression- -> O.SetScriptsExecutable- -> O.UMask- -> Bool- -> Bool- -> O.WantGuiPause- -> a)-applyAdvancedOpts- = O.reply- ^ O.ccApply- ^ O.happyForwarding- ^ O.sendmail- ^ O.useIndex- ^ O.compress- ^ O.setScriptsExecutable- ^ O.umask- ^ O.restrictPaths- ^ O.changesReverse- ^ O.pauseForGui--applyOpts :: DarcsOption a- (O.Verify- -> O.Reorder- -> Maybe Bool- -> O.DryRun- -> O.XmlOutput- -> [O.MatchFlag]- -> Maybe O.AllowConflicts- -> O.ExternalMerge- -> O.RunTest- -> O.LeaveTestDir- -> Maybe String- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> Maybe String- -> Maybe String- -> Bool- -> (Bool, Maybe String)- -> O.UseIndex- -> O.Compression- -> O.SetScriptsExecutable- -> O.UMask- -> Bool- -> Bool- -> O.WantGuiPause- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-applyOpts = applyBasicOpts `withStdOpts` applyAdvancedOpts- apply :: DarcsCommand [DarcsFlag] apply = DarcsCommand { commandProgramName = "darcs"@@ -241,7 +143,7 @@ , commandExtraArgHelp = ["<PATCHFILE>"] , commandCommand = applyCmd StandardPatchApplier , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = fileArgs , commandArgdefaults = const stdindefault , commandAdvancedOptions = odesc applyAdvancedOpts , commandBasicOptions = odesc applyBasicOpts@@ -249,25 +151,55 @@ , commandCheckOptions = ocheck applyOpts , commandParseOptions = onormalise applyOpts }+ where+ applyBasicOpts+ = O.verify+ ^ O.reorder+ ^ O.interactive+ ^ O.dryRunXml+ ^ O.matchSeveral+ ^ O.conflictsNo+ ^ O.externalMerge+ ^ O.runTest+ ^ O.leaveTestDir+ ^ O.repoDir+ ^ O.diffAlgorithm+ applyAdvancedOpts+ = O.reply+ ^ O.ccApply+ ^ O.happyForwarding+ ^ O.sendmail+ ^ O.useIndex+ ^ O.compress+ ^ O.setScriptsExecutable+ ^ O.umask+ ^ O.restrictPaths+ ^ O.changesReverse+ ^ O.pauseForGui+ applyOpts = applyBasicOpts `withStdOpts` applyAdvancedOpts applyCmd :: PatchApplier pa => pa -> (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () applyCmd _ _ _ [""] = fail "Empty filename argument given to apply!"-applyCmd patchApplier _ opts ["-"] = withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ repoJob patchApplier opts $ \patchProxy repository -> do+applyCmd patchApplier _ opts ["-"] =+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $+ repoJob patchApplier opts $ \patchProxy repository -> do -- for darcs users who try out 'darcs apply' without any arguments- putVerbose opts $ text "reading patch bundle from stdin..."- bundle <- gzReadStdin- applyCmdCommon patchApplier patchProxy opts bundle repository+ putVerbose opts $ text "reading patch bundle from stdin..."+ bundle <- gzReadStdin+ applyCmdCommon patchApplier patchProxy opts bundle repository -applyCmd patchApplier (_,o) opts [unfixed_patchesfile] = withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ repoJob patchApplier opts $ \patchProxy repository -> do- patchesfile <- fixUrl o unfixed_patchesfile- bundle <- gzFetchFilePS (toFilePath patchesfile) Uncachable- applyCmdCommon patchApplier patchProxy opts bundle repository+applyCmd patchApplier (_,o) opts [unfixed_patchesfile] =+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $+ repoJob patchApplier opts $ \patchProxy repository -> do+ patchesfile <- fixUrl o unfixed_patchesfile+ bundle <- gzFetchFilePS (toFilePath patchesfile) Uncachable+ applyCmdCommon patchApplier patchProxy opts bundle repository applyCmd _ _ _ _ = impossible applyCmdCommon :: forall rt pa p wR wU- . ( PatchApplier pa, RepoPatch p, ApplyState (PrimOf p) ~ Tree, ApplyState p ~ Tree+ . ( PatchApplier pa, RepoPatch p, ApplyState p ~ Tree , ApplierRepoTypeConstraint pa rt, IsRepoType rt ) => pa@@ -290,22 +222,22 @@ common :> _ <- return $ findCommonWithThem us them -- all patches that are in "them" and not in "common" need to be available; check that- let common_i = mapRL info $ newset2RL common- them_i = mapRL info $ newset2RL them+ let common_i = mapRL info $ patchSet2RL common+ them_i = mapRL info $ patchSet2RL them required = them_i \\ common_i -- FIXME quadratic? 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 ()- check NilRL bad = fail . renderString Encode $ vcat $ map showPatchInfoUI bad +++ check NilRL bad = fail . renderString $ vcat $ map displayPatchInfo bad ++ [ text "\nFATAL: Cannot apply this bundle. We are missing the above patches." ] - check (newset2RL them) []+ check (patchSet2RL them) [] (us':\/:them') <- return $ findUncommon us them (hadConflicts, Sealed their_ps)- <- if parseFlags conflictsOpt opts == Nothing -- skip conflicts+ <- if O.conflictsNo ? opts == Nothing -- skip conflicts then filterOutConflicts (reverseFL us') repository them' else return (False, Sealed them') when hadConflicts $ putStrLn "Skipping some patches which would cause conflicts."@@ -314,9 +246,9 @@ then putStrLn ("All new patches of the bundle cause conflicts. " ++ "Nothing to do.") >> exitSuccess else putStrLn ("All these patches have already been applied. " ++- "Nothing to do.") >> when (reorder opts /= O.Reorder) exitSuccess+ "Nothing to do.") >> when (reorder ? opts /= O.Reorder) exitSuccess - let direction = if doReverse opts then FirstReversed else First+ let direction = if changesReverse ? opts then FirstReversed else First context = selectionContext direction "apply" (patchSelOpts opts) Nothing Nothing (to_be_applied :> _) <- runSelection their_ps context applyPatches patchApplier patchProxy "apply" opts from_whom repository us' to_be_applied@@ -394,7 +326,7 @@ "key, or there was a GPG failure.\n" considerForwarding :: [DarcsFlag] -> B.ByteString -> IO Bool-considerForwarding opts bundle = case getReply opts of+considerForwarding opts bundle = case reply ? opts of Nothing -> return False Just from -> case break is_from (linesPS bundle) of (m1, f:m2) ->@@ -405,7 +337,7 @@ then return False -- Refuse possible email loop. else do scmd <- getSendmailCmd opts- if doHappyForwarding opts+ if happyForwarding ? opts then resendEmail from scmd bundle else sendEmailDoc f' from "A forwarded darcs patch" cc scmd (Just (empty,empty))@@ -421,12 +353,12 @@ patchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions patchSelOpts flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = parseFlags O.matchSeveral flags , S.interactive = maybeIsInteractive flags , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = O.NoSummary -- option not supported, use default- , S.withContext = isUnified flags+ , S.withContext = withContext ? flags } maybeIsInteractive :: [DarcsFlag] -> Bool
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.UI.Commands.Clone ( get , put@@ -28,8 +26,6 @@ import Prelude () import Darcs.Prelude -import Prelude hiding ( (^) )- import System.Directory ( doesDirectoryExist, doesFileExist , setCurrentDirectory ) import System.Exit ( ExitCode(..) )@@ -43,17 +39,20 @@ , commandAlias , putInfo )+import Darcs.UI.Completion ( noArgs ) import Darcs.UI.Flags( DarcsFlag( NewRepo , UpToPattern , UpToPatch+ , UpToHash , OnePattern , OnePatch+ , OneHash )- , toMatchFlags, useCache, umask, remoteRepos- , setDefault , DarcsFlag(Quiet), usePacks+ , matchAny, useCache, umask, remoteRepos+ , setDefault, quiet, usePacks , remoteDarcs, cloneKind, verbosity, setScriptsExecutable- , withWorkingDir, runPatchIndex )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags )+ , withWorkingDir, patchIndexNo )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.UI.Commands.Util ( getUniqueRepositoryName ) import Darcs.Repository ( cloneRepository )@@ -70,7 +69,7 @@ import Darcs.Patch.Bundle ( scanContextFile ) import Darcs.Patch.Dummy ( DummyPatch ) import Darcs.Patch.Set ( PatchSet, Origin )-import Darcs.Repository.Motd ( showMotd )+import Darcs.Repository.Prefs ( showMotd ) import Darcs.Util.Progress ( debugMessage ) import Darcs.Util.Printer ( text, ($$) ) import Darcs.Util.Path ( toFilePath, toPath, ioAbsoluteOrRemote, AbsolutePath )@@ -116,47 +115,6 @@ ] ++ cloneHelpTag ++ cloneHelpSSE -cloneBasicOpts :: DarcsOption a- (Maybe String- -> CloneKind- -> [O.MatchFlag]- -> Maybe Bool- -> O.SetScriptsExecutable- -> O.WithWorkingDir- -> a)-cloneBasicOpts = O.reponame- ^ O.partial- ^ O.matchOneContext- ^ O.setDefault- ^ O.setScriptsExecutable- ^ O.useWorkingDir--cloneAdvancedOpts :: DarcsOption a (Bool -> O.WithPatchIndex -> O.NetworkOptions -> a)-cloneAdvancedOpts = O.usePacks ^ O.patchIndex ^ O.network--cloneOpts :: DarcsOption a- (Maybe String- -> CloneKind- -> [O.MatchFlag]- -> Maybe Bool- -> O.SetScriptsExecutable- -> O.WithWorkingDir- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> Bool- -> O.WithPatchIndex- -> O.NetworkOptions- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-cloneOpts = cloneBasicOpts `withStdOpts` cloneAdvancedOpts- clone :: DarcsCommand [DarcsFlag] clone = DarcsCommand { commandProgramName = "darcs"@@ -167,7 +125,7 @@ , commandExtraArgHelp = ["<REPOSITORY>", "[<DIRECTORY>]"] , commandCommand = cloneCmd , commandPrereq = validContextFile- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc cloneAdvancedOpts , commandBasicOptions = odesc cloneBasicOpts@@ -175,6 +133,16 @@ , commandCheckOptions = ocheck cloneOpts , commandParseOptions = onormalise cloneOpts }+ where+ cloneBasicOpts+ = O.reponame+ ^ O.cloneKind+ ^ O.matchOneContext+ ^ O.setDefault+ ^ O.setScriptsExecutable+ ^ O.withWorkingDir+ cloneAdvancedOpts = O.usePacks ^ O.patchIndexNo ^ O.network+ cloneOpts = cloneBasicOpts `withStdOpts` cloneAdvancedOpts get :: DarcsCommand [DarcsFlag] get = commandAlias "get" Nothing clone@@ -199,7 +167,7 @@ debugMessage "Starting work on clone..." typed_repodir <- ioAbsoluteOrRemote inrepodir let repodir = toPath typed_repodir- unless (Quiet `elem` opts) $ showMotd repodir+ unless (quiet opts) $ showMotd repodir rfsource <- identifyRepoFormat repodir debugMessage $ "Found the format of "++repodir++"..." @@ -226,61 +194,63 @@ withTempDir "clone" $ \_ -> do putInfo opts $ text "Creating local clone..." currentDir <- getCurrentDirectory- cloneRepository repodir "local" (verbosity opts) (useCache opts)- CompleteClone (umask opts) (remoteDarcs opts)- (setScriptsExecutable opts)- (remoteRepos opts) (NoSetDefault True)- (toMatchFlags $ map convertUpToToOne opts)+ mysimplename <- makeRepoName True [] repodir -- give correct name to local clone+ cloneRepository repodir mysimplename (verbosity ? opts) (useCache ? opts)+ CompleteClone (umask ? opts) (remoteDarcs opts)+ (setScriptsExecutable ? opts)+ (remoteRepos ? opts) (NoSetDefault True)+ (matchAny ? map convertUpToToOne opts) rfsource- (withWorkingDir opts)- (runPatchIndex opts)- (usePacks opts)+ (withWorkingDir ? opts)+ (patchIndexNo ? opts)+ (usePacks ? opts) YesForgetParent setCurrentDirectory currentDir (scp, args) <- getSSH SCP- putInfo opts $ text "Transferring clone by SCP..."- r <- exec scp (args ++ ["-r", "local", repo]) (AsIs,AsIs,AsIs)- when (r /= ExitSuccess) $ fail $ "Problem during SCP transfer."+ putInfo opts $ text $ "Transferring clone using " ++ scp ++ "..."+ r <- exec scp (args ++ ["-r", mysimplename ++ "/", repo]) (AsIs,AsIs,AsIs)+ when (r /= ExitSuccess) $ fail $ "Problem during " ++ scp ++ " transfer." putInfo opts $ text "Cloning and transferring successful." Nothing -> do mysimplename <- makeRepoName True opts repodir- cloneRepository repodir mysimplename (verbosity opts) (useCache opts)- (cloneKind opts) (umask opts) (remoteDarcs opts)- (setScriptsExecutable opts)- (remoteRepos opts) (setDefault True opts)- (toMatchFlags $ map convertUpToToOne opts)+ cloneRepository repodir mysimplename (verbosity ? opts) (useCache ? opts)+ (cloneKind ? opts) (umask ? opts) (remoteDarcs opts)+ (setScriptsExecutable ? opts)+ (remoteRepos ? opts) (setDefault True opts)+ (matchAny ? map convertUpToToOne opts) rfsource- (withWorkingDir opts)- (runPatchIndex opts)- (usePacks opts)+ (withWorkingDir ? opts)+ (patchIndexNo ? opts)+ (usePacks ? opts) NoForgetParent putInfo opts $ text "Finished cloning." cloneCmd _ _ _ = fail "You must provide 'clone' with either one or two arguments." cloneToSSH :: [DarcsFlag] -> Maybe String-cloneToSSH fs = case parseFlags O.reponame fs of+cloneToSSH fs = case O.reponame ? fs of Nothing -> Nothing Just r -> if isSshUrl r then Just r else Nothing makeRepoName :: Bool -> [DarcsFlag] -> FilePath -> IO String-makeRepoName talkative dfs d =- case [ n | NewRepo n <- dfs] of- (n:_) ->- do exists <- doesDirectoryExist n- file_exists <- doesFileExist n- if exists || file_exists- then fail $ "Directory or file named '" ++ n ++ "' already exists."- else return n- [] ->- case dropWhile (=='.') $ reverse $- takeWhile (\c -> c /= '/' && c /= ':') $- dropWhile (=='/') $ reverse d of- "" -> getUniqueRepositoryName talkative "anonymous_repo"- base@('/':_) -> getUniqueRepositoryName talkative base -- Absolute- base -> do -- Relative- cwd <- getCurrentDirectory- getUniqueRepositoryName talkative (cwd ++ "/" ++ base)+makeRepoName talkative fs d =+ case O.reponame ? fs of+ Just n -> do+ exists <- doesDirectoryExist n+ file_exists <- doesFileExist n+ if exists || file_exists+ then fail $ "Directory or file named '" ++ n ++ "' already exists."+ else return n+ Nothing ->+ case mkName d of+ "" -> getUniqueRepositoryName talkative "anonymous_repo"+ base@('/':_) -> getUniqueRepositoryName talkative base -- Absolute+ base -- Relative+ -> do+ cwd <- getCurrentDirectory+ getUniqueRepositoryName talkative (cwd ++ "/" ++ base)+ where mkName = dropWhile (== '.') . reverse .+ takeWhile (not . (`elem` "/:")) . dropWhile (== '/') . reverse cloneHelpTag :: String cloneHelpTag =@@ -330,11 +300,14 @@ else return . Left $ "Context file " ++ ctxFilePath ++ " does not exist" +-- TODO getContext choses arbitrarily the first --context flag+-- should instead report an error when more than one is given+ -- | 'getContext' takes a list of flags and returns the context -- specified by @Context c@ in that list of flags, if any. -- This flag is present if darcs was invoked with @--context=FILE@ getContext :: [DarcsFlag] -> Maybe AbsolutePath-getContext fs = listToMaybe [ f | O.Context f <- toMatchFlags fs ]+getContext fs = listToMaybe [ f | O.Context f <- O.context ? fs ] -- The 'clone' command takes --to-patch and --to-match as arguments, -- but internally wants to handle them as if they were --patch and --match@@ -343,4 +316,5 @@ convertUpToToOne :: DarcsFlag -> DarcsFlag convertUpToToOne (UpToPattern p) = OnePattern p convertUpToToOne (UpToPatch p) = OnePatch p+convertUpToToOne (UpToHash p) = OneHash p convertUpToToOne f = f
@@ -15,7 +15,7 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, MagicHash, OverloadedStrings #-}+{-# LANGUAGE MagicHash, OverloadedStrings #-} module Darcs.UI.Commands.Convert ( convert ) where @@ -23,8 +23,11 @@ import Darcs.Prelude hiding ( readFile, lex ) import System.FilePath.Posix ( (</>) )-import System.Directory ( setCurrentDirectory, doesDirectoryExist, doesFileExist,- createDirectory, removeFile )+import System.Directory+ ( doesDirectoryExist+ , doesFileExist+ , removeFile+ ) import System.IO ( stdin ) import Data.IORef ( newIORef, modifyIORef, readIORef ) import Data.Char ( isSpace )@@ -35,7 +38,6 @@ import Control.Exception ( finally ) import Control.Applicative ( (<|>) ) -import GHC.Base ( unsafeCoerce# ) import System.Time ( toClockTime ) import Data.Maybe ( catMaybes, fromMaybe ) import qualified Data.IntMap as M@@ -49,6 +51,7 @@ import qualified Data.Attoparsec.ByteString.Char8 as A import Data.Attoparsec.ByteString.Char8( (<?>) ) +import Darcs.Util.ByteString ( decodeLocale ) import qualified Darcs.Util.Tree as T import qualified Darcs.Util.Tree.Monad as TM import Darcs.Util.Tree.Monad hiding ( createDirectory, exists, rename )@@ -57,14 +60,14 @@ , emptyTree, listImmediate, findTree ) import Darcs.Util.Path( anchorPath, appendPath, floatPath , parent, anchoredRoot- , AnchoredPath(..), Name(..)+ , AnchoredPath(..), makeName , 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.Exception ( clarifyErrors )+import Darcs.Util.Lock ( withNewDirectory ) import Darcs.Util.Prompt ( askUser ) import Darcs.Util.Printer ( text, ($$) ) import Darcs.Util.Printer.Color ( traceDoc )@@ -72,10 +75,9 @@ import Darcs.Patch.Depends ( getUncovered ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia, info, hopefully )-import Darcs.Patch ( IsRepoType, showPatch, fromPrim, fromPrims,- effect,- RepoPatch, apply, listTouchedFiles- , move )+import Darcs.Patch+ ( showPatch, ShowPatchFor(..), fromPrim, fromPrims+ , effect, RepoPatch, apply, listTouchedFiles, move ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Effect ( Effect ) import Darcs.Patch.Named@@ -86,63 +88,74 @@ 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,+ ( FL(..), RL(..), bunchFL, mapFL_FL, concatFL, mapRL, nullFL, (+>+), (+<+)- , reverseRL, reverseFL )+ , reverseRL, reverseFL, foldFL_M ) import Darcs.Patch.Witnesses.Sealed ( FlippedSeal(..), Sealed(..), unFreeLeft- , flipSeal, unsafeUnsealFlipped )+ , mapSeal, flipSeal, unsafeUnsealFlipped )+ import Darcs.Patch.Info ( piRename, piTag, isTag, PatchInfo, patchinfo, piName, piLog, piDate, piAuthor, makePatchname )-import Darcs.Patch.V1 ( RepoPatchV1 )-import Darcs.Patch.V2 ( RepoPatchV2 )+import qualified Darcs.Patch.V1 as V1 ( RepoPatchV1 )+import qualified Darcs.Patch.V1.Prim as V1 ( Prim(..) )+import qualified Darcs.Patch.V2 as V2 ( RepoPatchV2 )+import qualified Darcs.Patch.V2.Prim as V2 ( Prim(..) ) import Darcs.Patch.V1.Commute ( publicUnravel )-import Darcs.Patch.V1.Core ( RepoPatchV1(PP), isMerger )+import qualified Darcs.Patch.V1.Core as V1 ( 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.RepoType ( RepoType(..), IsRepoType(..), RebaseType(..) )+import Darcs.Patch.Set ( PatchSet(..), Tagged(..), patchSet2RL, patchSet2FL ) import Darcs.Patch.Progress ( progressFL ) -import Darcs.Repository.Flags ( UpdateWorking(..), Reorder (..), UseIndex(..), ScanKnown(..)- , AllowConflicts(..), ExternalMerge(..), WantGuiPause(..), PatchFormat(..)- , Compression(..), DryRun(NoDryRun), DiffAlgorithm(MyersDiff, PatienceDiff) )-import Darcs.Repository ( Repository, withRepoLock, RepoJob(..), withRepositoryDirectory,- createRepository, invalidateIndex,- tentativelyMergePatches,- createPristineDirectoryTree,- revertRepositoryChanges, finalizeRepositoryChanges,- applyToWorking- , readRepo, readTentativeRepo, cleanRepository )+import Darcs.Repository.Flags+ ( UpdateWorking(..)+ , Compression(..)+ , DiffAlgorithm(PatienceDiff) )+import Darcs.Repository+ ( Repository, RepoJob(..), withRepositoryLocation+ , createRepository, invalidateIndex, repoLocation+ , createPristineDirectoryTree, repoCache+ , revertRepositoryChanges, finalizeRepositoryChanges+ , applyToWorking, repoLocation, repoCache+ , readRepo, readTentativeRepo, cleanRepository+ , createRepositoryV2, EmptyRepository(..)+ , withUMaskFlag+ ) import qualified Darcs.Repository as R( setScriptsExecutable )+import Darcs.Repository.InternalTypes ( coerceR ) import Darcs.Repository.State( readRecorded ) import Darcs.Repository.Cache ( HashedDir( HashedPristineDir ) )-import Darcs.Repository.InternalTypes ( extractCache )-import Darcs.Repository.HashedRepo ( readHashedPristineRoot, addToTentativeInventory )+import Darcs.Repository.Hashed+ ( tentativelyAddPatch_+ , UpdatePristine(..)+ , readHashedPristineRoot+ , addToTentativeInventory ) import Darcs.Repository.HashedIO ( cleanHashdir )-import Darcs.Repository.Prefs( FileType(..) )+import Darcs.Repository.Prefs( FileType(..), showMotd ) import Darcs.Repository.Format(identifyRepoFormat, formatHas, RepoProperty(Darcs2))-import Darcs.Repository.Motd ( showMotd )-import Darcs.Util.Lock ( writeBinFile ) import Darcs.Util.External ( fetchFilePS, Cachable(Uncachable) ) import Darcs.Repository.Diff( treeDiff ) import Darcs.UI.External ( catchall ) import Darcs.UI.Flags- ( verbosity, useCache, umask, withWorkingDir, runPatchIndex+ ( verbosity, useCache, umask, withWorkingDir, patchIndexNo , DarcsFlag ( NewRepo )- , getRepourl, patchFormat+ , getRepourl, patchFormat, quiet ) 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 Darcs.UI.Completion ( noArgs )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O -#include "impossible.h"+type RepoPatchV1 = V1.RepoPatchV1 V1.Prim+type RepoPatchV2 = V2.RepoPatchV2 V2.Prim + convertDescription :: String convertDescription = "Convert repositories between various formats." @@ -212,46 +225,19 @@ ] convert :: DarcsCommand [DarcsFlag]-convert = SuperCommand {- commandProgramName = "darcs"+convert = SuperCommand+ { commandProgramName = "darcs" , commandName = "convert" , commandHelp = "" , commandDescription = convertDescription , commandPrereq = amInRepository- , commandSubCommands = [ normalCommand convertDarcs2,- normalCommand convertExport,- normalCommand convertImport- ]+ , commandSubCommands =+ [ normalCommand convertDarcs2+ , normalCommand convertExport+ , normalCommand convertImport+ ] } -convertDarcs2BasicOpts :: DarcsOption a (Maybe String -> O.SetScriptsExecutable -> O.WithWorkingDir -> a)-convertDarcs2BasicOpts = O.reponame ^ O.setScriptsExecutable ^ O.useWorkingDir--convertDarcs2AdvancedOpts :: DarcsOption a (O.NetworkOptions -> O.WithPatchIndex -> a)-convertDarcs2AdvancedOpts = O.network ^ O.patchIndex--convertDarcs2Opts :: DarcsOption a- (Maybe String- -> O.SetScriptsExecutable- -> O.WithWorkingDir- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.NetworkOptions- -> O.WithPatchIndex- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-convertDarcs2Opts = convertDarcs2BasicOpts `withStdOpts` convertDarcs2AdvancedOpts--convertDarcs2SilentOpts :: DarcsOption a (O.PatchFormat -> a)-convertDarcs2SilentOpts = O.patchFormat- convertDarcs2 :: DarcsCommand [DarcsFlag] convertDarcs2 = DarcsCommand { commandProgramName = "darcs"@@ -262,7 +248,7 @@ , commandExtraArgHelp = ["<SOURCE>", "[<DESTINATION>]"] , commandCommand = toDarcs2 , commandPrereq = \_ -> return $ Right ()- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc convertDarcs2AdvancedOpts , commandBasicOptions = odesc convertDarcs2BasicOpts@@ -270,31 +256,11 @@ , commandCheckOptions = ocheck convertDarcs2Opts , commandParseOptions = onormalise convertDarcs2Opts }--convertExportBasicOpts :: DarcsOption a- (Maybe String -> Maybe String -> Maybe String -> a)-convertExportBasicOpts = O.reponame ^ O.marks--convertExportAdvancedOpts :: DarcsOption a (O.NetworkOptions -> a)-convertExportAdvancedOpts = O.network--convertExportOpts :: DarcsOption a- (Maybe String- -> Maybe String- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.NetworkOptions- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-convertExportOpts = convertExportBasicOpts `withStdOpts` convertExportAdvancedOpts+ where+ convertDarcs2BasicOpts = O.reponame ^ O.setScriptsExecutable ^ O.withWorkingDir+ convertDarcs2AdvancedOpts = O.network ^ O.patchIndexNo+ convertDarcs2Opts = convertDarcs2BasicOpts `withStdOpts` convertDarcs2AdvancedOpts+ convertDarcs2SilentOpts = O.patchFormat convertExport :: DarcsCommand [DarcsFlag] convertExport = DarcsCommand@@ -306,7 +272,7 @@ , commandExtraArgHelp = [] , commandCommand = fastExport , commandPrereq = amInRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc convertExportAdvancedOpts , commandBasicOptions = odesc convertExportBasicOpts@@ -314,40 +280,10 @@ , commandCheckOptions = ocheck convertExportOpts , commandParseOptions = onormalise convertExportOpts }--convertImportBasicOpts :: DarcsOption a- (Maybe String- -> O.SetScriptsExecutable- -> O.PatchFormat- -> O.WithWorkingDir- -> a)-convertImportBasicOpts- = O.reponame- ^ O.setScriptsExecutable- ^ O.patchFormat- ^ O.useWorkingDir--convertImportAdvancedOpts :: DarcsOption a (O.WithPatchIndex -> a)-convertImportAdvancedOpts = O.patchIndex--convertImportOpts :: DarcsOption a- (Maybe String- -> O.SetScriptsExecutable- -> O.PatchFormat- -> O.WithWorkingDir- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.WithPatchIndex- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-convertImportOpts = convertImportBasicOpts `withStdOpts` convertImportAdvancedOpts+ where+ convertExportBasicOpts = O.reponame ^ O.marks+ convertExportAdvancedOpts = O.network+ convertExportOpts = convertExportBasicOpts `withStdOpts` convertExportAdvancedOpts convertImport :: DarcsCommand [DarcsFlag] convertImport = DarcsCommand@@ -359,7 +295,7 @@ , commandExtraArgHelp = ["[<DIRECTORY>]"] , commandCommand = fastImport , commandPrereq = \_ -> return $ Right ()- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc convertImportAdvancedOpts , commandBasicOptions = odesc convertImportBasicOpts@@ -367,15 +303,25 @@ , commandCheckOptions = ocheck convertImportOpts , commandParseOptions = onormalise convertImportOpts }+ where+ convertImportBasicOpts+ = O.reponame+ ^ O.setScriptsExecutable+ ^ O.patchFormat+ ^ O.withWorkingDir+ convertImportAdvancedOpts = O.patchIndexNo+ convertImportOpts = convertImportBasicOpts `withStdOpts` convertImportAdvancedOpts toDarcs2 :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-toDarcs2 fps opts [inrepodir, outname] = toDarcs2 fps (NewRepo outname:opts) [inrepodir]-toDarcs2 _ opts [inrepodir] = do-+toDarcs2 _ opts' args = do+ (inrepodir, opts) <-+ case args of+ [arg1, arg2] -> return (arg1, NewRepo arg2:opts')+ [arg1] -> return (arg1, opts')+ _ -> fail "You must provide either one or two arguments." typed_repodir <- ioAbsoluteOrRemote inrepodir let repodir = toPath typed_repodir - --test for converting darcs-2 repository format <- identifyRepoFormat repodir when (formatHas Darcs2 format) $ fail "Repository is already in darcs 2 format." @@ -385,42 +331,46 @@ vow' <- askUser ("by typing `" ++ vow ++ "': ") when (vow' /= vow) $ fail "User didn't understand the consequences." - unless (parseFlags O.verbosity opts == O.Quiet) $ showMotd repodir+ unless (quiet opts) $ showMotd repodir+ mysimplename <- makeRepoName opts repodir- createDirectory mysimplename- setCurrentDirectory mysimplename- 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 . hopefully) $ newset2FL theirstuff- outOfOrderTags = catMaybes $ mapRL oot $ newset2RL theirstuff+ withUMaskFlag (umask ? opts) $ withNewDirectory mysimplename $ do+ repo <- createRepositoryV2+ (withWorkingDir ? opts) (patchIndexNo ? opts) (O.useCache ? opts)+ revertRepositoryChanges repo NoUpdateWorking++ withRepositoryLocation (useCache ? opts) repodir $ V1Job $ \other -> do+ theirstuff <- readRepo other+ let patches = mapFL_FL (convertNamed . hopefully) $ patchSet2FL theirstuff+ outOfOrderTags = catMaybes $ mapRL oot $ patchSet2RL theirstuff where oot t = if isTag (info t) && info t `notElem` inOrderTags theirstuff 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 :: 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- IsEq -> y :>: NilFL- NotEq ->- traceDoc (text "lossy conversion:" $$- showPatch x)- fromPrims (effect x)- Nothing -> traceDoc (text- "lossy conversion of complicated conflict:" $$- showPatch x)- fromPrims (effect x)- convertOne (PP x) = fromPrim x :>: NilFL+ primV1toV2 = V2.Prim . V1.unPrim+ convertOne :: RepoPatchV1 wX wY -> FL RepoPatchV2 wX wY+ convertOne x | V1.isMerger x =+ let ex = mapFL_FL primV1toV2 (effect x) in+ case mergeUnravelled $ map (mapSeal (mapFL_FL primV1toV2)) $ publicUnravel x of+ Just (FlippedSeal y) ->+ case effect y =/\= ex of+ IsEq -> y :>: NilFL+ NotEq ->+ traceDoc (text "lossy conversion:" $$+ showPatch ForDisplay x)+ fromPrims ex+ Nothing -> traceDoc (text+ "lossy conversion of complicated conflict:" $$+ showPatch ForDisplay x)+ fromPrims ex+ convertOne (V1.PP x) = fromPrim (primV1toV2 x) :>: NilFL convertOne _ = impossible- convertFL :: FL (RepoPatchV1 Prim) wX wY -> FL (RepoPatchV2 Prim) wX wY+ convertFL :: FL RepoPatchV1 wX wY -> FL RepoPatchV2 wX wY convertFL = concatFL . mapFL_FL convertOne- convertNamed :: WrappedNamed ('RepoType 'NoRebase) (RepoPatchV1 Prim) wX wY- -> PatchInfoAnd ('RepoType 'NoRebase) (RepoPatchV2 Prim) wX wY+ convertNamed :: WrappedNamed ('RepoType 'NoRebase) RepoPatchV1 wX wY+ -> PatchInfoAnd ('RepoType 'NoRebase) RepoPatchV2 wX wY convertNamed (NormalP n) = n2pia $ NormalP $ adddeps (infopatch (convertInfo $ patch2patchinfo n) $@@ -428,24 +378,9 @@ (map convertInfo $ concatMap fixDep $ getdeps n) convertInfo n | n `elem` inOrderTags theirstuff = n | otherwise = maybe n (\t -> piRename n ("old tag: "++t)) $ piTag n- 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 ('RepoType 'NoRebase) (RepoPatchV2 Prim) wA wB wA- Sealed pw <- tentativelyMergePatches repository2 "convert"- YesAllowConflicts NoUpdateWorking- NoExternalMerge NoWantGuiPause- GzipCompression (verbosity opts)- NoReorder- (UseIndex, ScanKnown, MyersDiff)- NilFL xs- finalizeRepositoryChanges repository2 NoUpdateWorking GzipCompression -- this is to clean out pristine.hashed- revertRepositoryChanges repository2 NoUpdateWorking- _ <- revertable $ applyToWorking repository2 (verbosity opts) pw- invalidateIndex repository2- sequence_ $ mapFL applySome $ bunchFL 100 $ progressFL "Converting patch" patches- invalidateIndex repository- revertable $ createPristineDirectoryTree repository "." (withWorkingDir opts)++ -- Note: we use bunchFL so we can commit every 100 patches+ _ <- applyAll opts repo $ bunchFL 100 $ progressFL "Converting patch" patches when (parseFlags O.setScriptsExecutable opts == O.YesSetScriptsExecutable) R.setScriptsExecutable @@ -455,13 +390,71 @@ `catchall` return () putInfo opts $ text "Finished converting."- where revertable x = x `clarifyErrors` unlines- ["An error may have left your new working directory an inconsistent",- "but recoverable state. You should be able to make the new",- "repository consistent again by running darcs revert -a."]+ where+ applyOne :: (RepoPatch p, ApplyState p ~ Tree)+ => [DarcsFlag]+ -> W2 (Repository rt p wR) wX+ -> PatchInfoAnd rt p wX wY+ -> IO (W2 (Repository rt p wR) wY)+ applyOne opts (W2 r) x = do+ r' <- tentativelyAddPatch_ (updatePristine opts) r+ GzipCompression (verbosity ? opts) (updateWorking opts) x+ r'' <- withTryAgainMsg $ applyToWorking r' (verbosity ? opts) (effect x)+ invalidateIndex r''+ return (W2 r'') -toDarcs2 _ _ _ = fail "You must provide either one or two arguments."+ applySome opts (W3 r) xs = do+ r' <- unW2 <$> foldFL_M (applyOne opts) (W2 r) xs+ -- commit after applying a bunch of patches+ finalizeRepositoryChanges r' (updateWorking opts) GzipCompression+ revertRepositoryChanges r' (updateWorking opts)+ -- finalizeRepositoryChanges and revertRepositoryChanges+ -- do not (yet?) return a repo with properly coerced witnesses.+ -- We should have+ --+ -- > finalizeRepositoryChanges :: ... wR wU wT -> ... wT wU wT+ --+ -- and+ --+ -- > revertRepositoryChanges :: ... wR wU wT -> ... wR wU wR+ --+ -- This is why we must coerce here:+ return (W3 (coerceR r')) + applyAll :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => [DarcsFlag]+ -> Repository rt p wX wX wX+ -> FL (FL (PatchInfoAnd rt p)) wX wY+ -> IO (Repository rt p wY wY wY)+ applyAll opts r xss = unW3 <$> foldFL_M (applySome opts) (W3 r) xss++ updatePristine :: [DarcsFlag] -> UpdatePristine+ updatePristine opts =+ case withWorkingDir ? opts of+ O.WithWorkingDir -> UpdatePristine+ -- this should not be necessary but currently is, because+ -- some commands (e.g. send) cannot cope with a missing pristine+ -- even if the repo is marked as having no working tree+ O.NoWorkingDir -> {- DontUpdatePristineNorRevert -}UpdatePristine++ updateWorking :: [DarcsFlag] -> UpdateWorking+ updateWorking opts =+ case withWorkingDir ? opts of+ O.WithWorkingDir -> YesUpdateWorking+ O.NoWorkingDir -> NoUpdateWorking++ withTryAgainMsg :: IO a -> IO a+ withTryAgainMsg x = x `clarifyErrors` unlines+ [ "An error occurred while applying patches to the working tree."+ , "You may have more luck if you supply --no-working-dir." ]++-- | Need this to make 'foldFL_M' work with a function that changes+-- the last two (identical) witnesses at the same time.+newtype W2 r wX = W2 {unW2 :: r wX wX}++-- | Similarly for when the function changes all three witnesses.+newtype W3 r wX = W3 {unW3 :: r wX wX wX}+ makeRepoName :: [DarcsFlag] -> FilePath -> IO String makeRepoName (NewRepo n:_) _ = do exists <- doesDirectoryExist n@@ -503,7 +496,7 @@ marks <- case parseFlags O.readMarks opts of Nothing -> return emptyMarks Just f -> readMarks f- newMarks <- withRepositoryDirectory (useCache opts) repodir $ RepoJob $ \repo -> fastExport' repo marks+ newMarks <- withRepositoryLocation (useCache ? opts) repodir $ RepoJob $ \repo -> fastExport' repo marks case parseFlags O.writeMarks opts of Nothing -> return () Just f -> writeMarks f newMarks@@ -514,7 +507,7 @@ putStrLn "progress (reading repository)" patchset <- readRepo repo marksref <- newIORef marks- let patches = newset2FL patchset+ let patches = patchSet2FL patchset tags = inOrderTags patchset mark :: (PatchInfoAnd rt p) x y -> Int -> TreeIO () mark p n = liftIO $ do putStrLn $ "mark :" ++ show n@@ -543,7 +536,7 @@ `finally` do putStrLn "progress (cleaning up)" current <- readHashedPristineRoot repo- cleanHashdir (extractCache repo) HashedPristineDir $ catMaybes [current]+ cleanHashdir (repoCache repo) HashedPristineDir $ catMaybes [current] putStrLn "progress done" dumpPatches :: (RepoPatch p, ApplyState p ~ Tree)@@ -623,7 +616,7 @@ -> (PatchInfoAnd rt p) x y -> Int -> TreeIO () dumpPatch mark p n =- do dumpBits [ BLC.pack $ "progress " ++ show n ++ ": " ++ piName (info p)+ do dumpBits [ BLU.fromString $ "progress " ++ show n ++ ": " ++ piName (info p) , "commit refs/heads/master" ] mark p n dumpBits [ BLU.fromString $ "committer " ++ patchAuthor p ++ " " ++ patchDate p@@ -722,14 +715,16 @@ fastImport :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () fastImport _ opts [outrepo] =- do createDirectory outrepo- withCurrentDirectory outrepo $ do- 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- createPristineDirectoryTree repo "." (withWorkingDir opts)- return marks+ withUMaskFlag (umask ? opts) $ withNewDirectory outrepo $ do+ EmptyRepository repo <- createRepository+ (patchFormat ? opts)+ (withWorkingDir ? opts)+ (patchIndexNo ? opts)+ (useCache ? opts)+ -- TODO implement --dry-run, which would be read-only?+ marks <- fastImport' repo emptyMarks+ createPristineDirectoryTree repo "." (withWorkingDir ? opts)+ return marks fastImport _ _ _ = fail "I need exactly one output repository." fastImport' :: forall rt p r u . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) =>@@ -749,8 +744,8 @@ -- sort marks into buckets, since there can be a *lot* of them markpath :: Int -> AnchoredPath markpath n = floatPath (darcsdir </> "marks")- `appendPath` (Name $ BC.pack $ show (n `div` 1000))- `appendPath` (Name $ BC.pack $ show (n `mod` 1000))+ `appendPath` (makeName $ show (n `div` 1000))+ `appendPath` (makeName $ show (n `mod` 1000)) makeinfo author message tag = do let (name, log) = case BC.unpack message of@@ -765,16 +760,18 @@ addtag author msg = do info_ <- makeinfo author msg True gotany <- liftIO $ doesFileExist $ darcsdir </> "tentative_hashed_pristine"- deps <- if gotany then liftIO $ getUncovered `fmap` readTentativeRepo repo+ deps <- if gotany then liftIO $+ getUncovered `fmap`+ readTentativeRepo repo (repoLocation repo) else return []- let ident = NilFL :: FL (RepoPatchV2 Prim) cX cX+ let ident = NilFL :: FL RepoPatchV2 cX cX patch = NormalP (adddeps (infopatch info_ ident) deps)- void $ liftIO $ addToTentativeInventory (extractCache repo)+ void $ liftIO $ addToTentativeInventory (repoCache repo) GzipCompression (n2pia patch) -- processing items updateHashes = do- let nodarcs = \(AnchoredPath (Name x:_)) _ -> x /= BC.pack darcsdir+ let nodarcs = \(AnchoredPath (x:_)) _ -> x /= makeName darcsdir hashblobs (File blob@(T.Blob con NoHash)) = do hash <- sha256 `fmap` readBlob blob return $ File (T.Blob con hash)@@ -809,7 +806,7 @@ process :: State p -> Object -> TreeIO (State p) process s (Progress p) = do- liftIO $ putStrLn ("progress " ++ BC.unpack p)+ liftIO $ putStrLn ("progress " ++ decodeLocale p) return s process (Toplevel _ _) End = do@@ -826,7 +823,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 $ decodeLocale msg) return (Toplevel n b) process (Toplevel n _) (Reset branch from) =@@ -919,7 +916,7 @@ {- 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)+ void $ liftIO $ addToTentativeInventory (repoCache repo) GzipCompression (n2pia patch) case mark of Nothing -> return ()
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.UI.Commands.Diff ( diffCommand, getDiffDoc ) where import Prelude ()@@ -39,10 +37,9 @@ , execPipeIgnoreError ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )-import Darcs.UI.Flags- ( DarcsFlag ( AfterPatch, DiffCmd, LastN )- , wantGuiPause, useCache, fixSubPaths )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags )+import Darcs.UI.Completion ( knownFileArgs )+import Darcs.UI.Flags ( DarcsFlag, wantGuiPause, useCache, fixSubPaths )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.Flags ( WantGuiPause (..), DiffAlgorithm(MyersDiff) ) import Darcs.Patch.PatchInfoAnd ( info, n2pia )@@ -55,22 +52,21 @@ , matchSecondPatchset ) import Darcs.Repository ( withRepository, RepoJob(..), readRepo )-import Darcs.Repository.State ( readUnrecorded, restrictSubpaths- , readRecorded, unrecordedChanges- , UseIndex(..), ScanKnown(..), applyTreeFilter )+import Darcs.Repository.State+ ( readUnrecorded, restrictSubpaths+ , readRecorded, unrecordedChanges+ , UseIndex(..), ScanKnown(..), applyTreeFilter+ ) 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, 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.Patch.Set ( PatchSet(..), patchSet2RL )+import Darcs.Patch.Info ( PatchInfo, displayPatchInfo ) import Darcs.Util.Lock ( withTempDir )-import Darcs.Util.Printer ( Doc, putDoc, vcat, empty, RenderMode(..), ($$) )--#include "impossible.h"-+import Darcs.Util.Printer ( Doc, putDoc, vcat, empty, ($$) ) diffDescription :: String diffDescription = "Create a diff between two versions of the repository."@@ -110,43 +106,6 @@ "\n" ++ "If this option is used, `--diff-opts` is ignored.\n" -diffBasicOpts :: DarcsOption a- ([O.MatchFlag]- -> O.ExternalDiff- -> Bool- -> Maybe String- -> Bool- -> a)-diffBasicOpts- = O.matchRange- ^ O.extDiff- ^ O.unidiff- ^ O.workingRepoDir- ^ O.storeInMemory--diffAdvancedOpts :: DarcsOption a (WantGuiPause -> a)-diffAdvancedOpts = O.pauseForGui--diffOpts :: DarcsOption a- ([O.MatchFlag]- -> O.ExternalDiff- -> Bool- -> Maybe String- -> Bool- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> WantGuiPause- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-diffOpts = diffBasicOpts `withStdOpts` diffAdvancedOpts- diffCommand :: DarcsCommand [DarcsFlag] diffCommand = DarcsCommand { commandProgramName = "darcs"@@ -157,7 +116,7 @@ , commandExtraArgHelp = ["[FILE or DIRECTORY]..."] , commandCommand = diffCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = knownFileArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc diffAdvancedOpts , commandBasicOptions = odesc diffBasicOpts@@ -165,11 +124,18 @@ , commandCheckOptions = ocheck diffOpts , commandParseOptions = onormalise diffOpts }+ where+ diffBasicOpts+ = O.matchRange+ ^ O.extDiff+ ^ O.repoDir+ ^ O.storeInMemory+ diffAdvancedOpts = O.pauseForGui+ diffOpts = diffBasicOpts `withStdOpts` diffAdvancedOpts -getDiffOpts :: [DarcsFlag] -> [String]-getDiffOpts fs = addUnified $ otherDiffOpts fs where- addUnified = if parseFlags O.unidiff fs then ("-u":) else id- otherDiffOpts = O._diffOpts . parseFlags O.extDiff+getDiffOpts :: O.ExternalDiff -> [String]+getDiffOpts O.ExternalDiff {O.diffOpts=os,O.diffUnified=u} = addUnified os where+ addUnified = if u then ("-u":) else id -- | Returns the command we should use for diff as a tuple (command, arguments). -- This will either be whatever the user specified via --diff-command or the@@ -177,20 +143,21 @@ -- user's diff-command, hence the possibility for failure with an exception. getDiffCmdAndArgs :: String -> [DarcsFlag] -> String -> String -> Either String (String, [String])-getDiffCmdAndArgs cmd opts f1 f2 = helper opts where- helper (DiffCmd c:_) =- case parseCmd [ ('1', f1) , ('2', f2) ] c of- Left err -> Left $ show err- Right ([],_) -> bug "parseCmd should never return empty list"- Right (h:t,_) -> Right (h,t)- helper [] = -- if no command specified, use 'diff'- Right (cmd, "-rN":getDiffOpts opts++[f1,f2])- helper (_:t) = helper t+getDiffCmdAndArgs cmd opts f1 f2 = helper (O.extDiff ? opts) where+ helper extDiff =+ case O.diffCmd extDiff of+ Just c ->+ case parseCmd [ ('1', f1) , ('2', f2) ] c of+ Left err -> Left $ show err+ Right ([],_) -> bug "parseCmd should never return empty list"+ Right (h:t,_) -> Right (h,t)+ Nothing -> -- if no command specified, use 'diff'+ Right (cmd, "-rN":getDiffOpts extDiff++[f1,f2]) diffCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () diffCmd fps opts args- | not (null [i | LastN i <- opts]) &&- not (null [p | AfterPatch p <- opts]) =+ | not (null (O.matchLast ? opts)) &&+ not (null (O.matchFrom ? opts)) = fail $ "using --patch and --last at the same time with the 'diff'" ++ " command doesn't make sense. Use --from-patch to create a diff" ++ " from this patch to the present, or use just '--patch' to view" ++@@ -202,14 +169,16 @@ doDiff opts msubpaths = getDiffDoc opts msubpaths >>= putDoc getDiffDoc :: [DarcsFlag] -> Maybe [SubPath] -> IO Doc-getDiffDoc opts msubpaths = withRepository (useCache opts) $ RepoJob $ \repository -> do+getDiffDoc opts msubpaths = withRepository (useCache ? opts) $ RepoJob $ \repository -> do formerdir <- getCurrentDirectory let thename = takeFileName formerdir patchset <- readRepo repository - unrecorded <- fromPrims `fmap` unrecordedChanges (UseIndex, ScanKnown, MyersDiff) repository msubpaths+ unrecorded <- fromPrims `fmap` unrecordedChanges (UseIndex, ScanKnown, MyersDiff)+ O.NoLookForMoves O.NoLookForReplaces+ repository msubpaths unrecorded' <- n2pia `fmap` anonymous unrecorded let matchFlags = parseFlags O.matchRange opts@@ -270,7 +239,7 @@ let pausingForGui = (wantGuiPause opts == YesWantGuiPause) in do when pausingForGui $ putStrLn $ "Running command '" ++ unwords (d_cmd:d_args) ++ "'"- output <- execPipeIgnoreError Encode d_cmd d_args empty+ output <- execPipeIgnoreError d_cmd d_args empty when pausingForGui $ askEnter "Hit return to move on..." return output@@ -278,7 +247,7 @@ 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+ infos = mapRL info . patchSet2RL handle (match_cond, do_match) | match_cond matchFlags = unseal infos (do_match matchFlags ps) | otherwise = infos ps@@ -286,5 +255,5 @@ \\ handle (firstMatch, matchFirstPatchset) changelog :: [PatchInfo] -> Doc-changelog pis = vcat $ map showPatchInfoUI pis+changelog pis = vcat $ map displayPatchInfo pis
@@ -49,37 +49,40 @@ import Codec.Archive.Zip ( emptyArchive, fromArchive, addEntryToArchive, toEntry ) import Darcs.Util.External ( fetchFilePS, Cachable( Uncachable ) ) import Darcs.Util.Global ( darcsdir )-import Darcs.Repository.HashedRepo ( inv2pris )+import Darcs.Repository.Hashed ( peekPristineHash ) import Darcs.Repository.HashedIO ( pathsAndContents )-import Darcs.Repository.InternalTypes ( Repository (..) ) import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString as B-import Darcs.UI.Flags- ( DarcsFlag(Verbose, Quiet, DistName, DistZip, SetScriptsExecutable), useCache )+import Darcs.UI.Flags as F ( DarcsFlag, useCache )+import qualified Darcs.UI.Flags as F ( setScriptsExecutable ) import Darcs.UI.Options- ( DarcsOption, (^), oid, odesc, ocheck, onormalise- , defaultFlags, parseFlags+ ( (^), oid, odesc, ocheck, onormalise+ , defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O -import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )+import Darcs.UI.Commands+ ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository+ , putVerbose, putInfo+ )+import Darcs.UI.Completion ( noArgs ) import Darcs.Util.Lock ( withTempDir ) import Darcs.Patch.Match ( haveNonrangeMatch ) import Darcs.Repository.Match ( getNonrangeMatch )-import Darcs.Repository ( withRepository, withRepositoryDirectory, RepoJob(..),- setScriptsExecutable, repoPatchType,+import Darcs.Repository ( withRepository, withRepositoryLocation, RepoJob(..),+ setScriptsExecutable, repoPatchType, repoCache, createPartialsPristineDirectoryTree ) import Darcs.Repository.Prefs ( getPrefval ) import Darcs.Util.DateTime ( getCurrentTime, toSeconds ) import Darcs.Util.Path ( AbsolutePath, toFilePath ) import Darcs.Util.File ( withCurrentDirectory )+import Darcs.Util.Printer ( text, vcat ) distDescription :: String distDescription = "Create a distribution archive." - distHelp :: String distHelp = unlines [ "`darcs dist` creates a compressed archive in the repository's root"@@ -99,42 +102,6 @@ , "If `--zip` is used, matchers and the predist command are ignored." ] -distBasicOpts :: DarcsOption a- (Maybe String- -> Bool- -> Maybe String- -> [O.MatchFlag]- -> O.SetScriptsExecutable- -> Bool- -> a)-distBasicOpts- = O.distname- ^ O.distzip- ^ O.workingRepoDir- ^ O.matchUpToOne- ^ O.setScriptsExecutable- ^ O.storeInMemory--distOpts :: DarcsOption a- (Maybe String- -> Bool- -> Maybe String- -> [O.MatchFlag]- -> O.SetScriptsExecutable- -> Bool- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-distOpts = distBasicOpts `withStdOpts` oid- dist :: DarcsCommand [DarcsFlag] dist = DarcsCommand { commandProgramName = "darcs"@@ -145,7 +112,7 @@ , commandExtraArgHelp = [] , commandCommand = distCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = odesc distBasicOpts@@ -153,16 +120,22 @@ , commandCheckOptions = ocheck distOpts , commandParseOptions = onormalise distOpts }+ where+ distBasicOpts+ = O.distname+ ^ O.distzip+ ^ O.repoDir+ ^ O.matchUpToOne+ ^ O.setScriptsExecutable+ ^ O.storeInMemory+ distOpts = distBasicOpts `withStdOpts` oid -distCmd :: (AbsolutePath, AbsolutePath)- -> [DarcsFlag]- -> [String]- -> IO ()-distCmd _ opts _ | DistZip `elem` opts = doFastZip opts-distCmd _ opts _ = withRepository (useCache opts) $ RepoJob $ \repository -> do+distCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()+distCmd _ opts _ | O.distzip ? opts = doFastZip opts+distCmd _ opts _ = withRepository (useCache ? opts) $ RepoJob $ \repository -> do let matchFlags = parseFlags O.matchUpToOne opts formerdir <- getCurrentDirectory- let distname = getDistName formerdir [x | DistName x <- opts]+ let distname = getDistName formerdir (O.distname ? opts) predist <- getPrefval "predist" let resultfile = formerdir </> distname ++ ".tar.gz" withTempDir "darcsdist" $ \tempdir -> do@@ -174,15 +147,15 @@ ec <- case predist of Nothing -> return ExitSuccess Just pd -> system pd if ec == ExitSuccess- then- do- withCurrentDirectory ddir $- when (SetScriptsExecutable `elem` opts) setScriptsExecutable- doDist opts tempdir ddir resultfile- else- do- putStrLn "Dist aborted due to predist failure"- exitWith ec+ then do+ withCurrentDirectory ddir $+ when+ (F.setScriptsExecutable ? opts == O.YesSetScriptsExecutable)+ setScriptsExecutable+ doDist opts tempdir ddir resultfile+ else do+ putStrLn "Dist aborted due to predist failure"+ exitWith ec -- | This function performs the actual distribution action itself.@@ -193,37 +166,36 @@ setCurrentDirectory (toFilePath tempdir) let safeddir = safename $ takeFileName $ toFilePath ddir entries <- pack "." [safeddir]- when (Verbose `elem` opts) $ putStr $ unlines $ map entryPath entries+ putVerbose opts $ vcat $ map (text . entryPath) entries writeFile resultfile $ compress $ write entries- when (Quiet `notElem` opts) $ putStrLn $ "Created dist as " ++ resultfile+ putInfo opts $ text $ "Created dist as " ++ resultfile where safename n@(c:_) | isAlphaNum c = n safename n = "./" ++ n -getDistName :: FilePath -> [String] -> FilePath-getDistName _ (dn:_) = dn+getDistName :: FilePath -> Maybe String -> FilePath+getDistName _ (Just dn) = dn getDistName currentDirectory _ = takeFileName currentDirectory -doFastZip :: [DarcsFlag]- -> IO ()+doFastZip :: [DarcsFlag] -> IO () doFastZip opts = do currentdir <- getCurrentDirectory- let distname = getDistName currentdir [x | DistName x <- opts] + let distname = getDistName currentdir (O.distname ? opts) let resultfile = currentdir </> distname ++ ".zip" doFastZip' opts currentdir (writeFile resultfile)- when (Quiet `notElem` opts) $ putStrLn $ "Created " ++ resultfile+ putInfo opts $ text $ "Created " ++ resultfile doFastZip' :: [DarcsFlag] -- ^ Flags/options -> FilePath -- ^ The path to the repository -> (BL.ByteString -> IO a) -- ^ An action to perform on the archive contents -> IO a-doFastZip' opts path act = withRepositoryDirectory (useCache opts) path $ RepoJob $ \(Repo _ _ _ c) -> do- when (SetScriptsExecutable `elem` opts) $+doFastZip' opts path act = withRepositoryLocation (useCache ? opts) path $ RepoJob $ \repo -> do+ when (F.setScriptsExecutable ? opts == O.YesSetScriptsExecutable) $ putStrLn "WARNING: Zip archives cannot store executable flag." - let distname = getDistName path [x | DistName x <- opts]+ let distname = getDistName path (O.distname ? opts) i <- fetchFilePS (path </> darcsdir </> "hashed_inventory") Uncachable- pristine <- pathsAndContents (distname ++ "/") c (inv2pris i)+ pristine <- pathsAndContents (distname ++ "/") (repoCache repo) (peekPristineHash i) epochtime <- toSeconds `fmap` getCurrentTime let entries = [ toEntry filepath epochtime (toLazy contents) | (filepath,contents) <- pristine ] let archive = foldr addEntryToArchive emptyArchive entries
@@ -20,8 +20,6 @@ -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -- SOFTWARE. -{-# LANGUAGE CPP #-}- module Darcs.UI.Commands.GZCRCs ( gzcrcs , doCRCWarnings@@ -30,7 +28,6 @@ import Prelude () import Darcs.Prelude -import Prelude hiding ( (^) ) import Control.Monad ( when, unless, forM_ ) import Control.Monad.Trans ( liftIO ) import Control.Monad.Writer ( runWriterT, tell )@@ -44,25 +41,25 @@ import Darcs.Util.File ( getRecursiveContentsFullPath ) import Darcs.Util.ByteString ( isGZFile, gzDecompress ) import Darcs.Util.Global ( getCRCWarnings, resetCRCWarnings )-import Darcs.Repository ( Repository, withRepository, RepoJob(..) )+import Darcs.Repository ( Repository, withRepository, RepoJob(..), repoCache ) -- This command needs access beyond the normal repository APIs (to -- get at the caches and inspect them directly) -- Could move the relevant code into Darcs.Repository modules -- but it doesn't really seem worth it. import Darcs.Repository.Cache ( Cache(..), writable, isThisRepo, hashedFilePath, allHashedDirs )-import Darcs.Repository.InternalTypes ( extractCache ) 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 )+import Darcs.UI.Commands+ ( DarcsCommand(..), withStdOpts, nodefaults, amInRepository+ , putInfo, putVerbose+ )+import Darcs.UI.Completion ( noArgs )+import Darcs.UI.Options ( (^), oid, odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Util.Path ( AbsolutePath )-import Darcs.UI.Flags- ( DarcsFlag( Quiet, Verbose, Check, Repair, JustThisRepo )- , useCache )+import Darcs.UI.Flags ( DarcsFlag, useCache ) import Darcs.Util.Text ( formatText )-import Darcs.Util.Printer ( putDocLn, text )+import Darcs.Util.Printer ( text ) gzcrcsHelp :: String gzcrcsHelp = formatText 80@@ -112,26 +109,6 @@ gzcrcsDescription = "Check or repair the CRCs of compressed files in the " ++ "repository." -gzcrcsBasicOpts :: DarcsOption a (Maybe O.GzcrcsAction -> Bool -> Maybe String -> a)-gzcrcsBasicOpts = O.gzcrcsActions ^ O.justThisRepo ^ O.workingRepoDir--gzcrcsOpts :: DarcsOption a- (Maybe O.GzcrcsAction- -> Bool- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-gzcrcsOpts = gzcrcsBasicOpts `withStdOpts` oid- gzcrcs :: DarcsCommand [DarcsFlag] gzcrcs = DarcsCommand { commandProgramName = "darcs"@@ -142,7 +119,7 @@ , commandExtraArgHelp = [] , commandCommand = gzcrcsCmd , commandPrereq = amInRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = odesc gzcrcsBasicOpts@@ -150,19 +127,23 @@ , commandCheckOptions = ocheck gzcrcsOpts , commandParseOptions = onormalise gzcrcsOpts }+ where+ gzcrcsBasicOpts = O.gzcrcsActions ^ O.justThisRepo ^ O.repoDir+ gzcrcsOpts = gzcrcsBasicOpts `withStdOpts` oid gzcrcsCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-gzcrcsCmd _ opts _ | Check `elem` opts || Repair `elem` opts =- withRepository (useCache opts) (RepoJob (gzcrcs' opts))-gzcrcsCmd _ _ _ = error "You must specify --check or --repair for gzcrcs"+gzcrcsCmd _ opts _ =+ case O.gzcrcsActions ? opts of+ Nothing -> fail "You must specify --check or --repair for gzcrcs"+ Just action -> withRepository (useCache ? opts) (RepoJob (gzcrcs' action opts)) -gzcrcs' :: (RepoPatch p) => [DarcsFlag] -> Repository rt p wR wU wT -> IO ()-gzcrcs' opts repo = do+gzcrcs' :: O.GzcrcsAction -> [DarcsFlag] -> Repository rt p wR wU wT -> IO ()+gzcrcs' action 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 -- the message up front. warnRelatedRepos <- newIORef $ not isJustThisRepo- let Ca locs = extractCache repo+ let Ca locs = repoCache repo (_, Any checkFailed) <- runWriterT $ forM_ locs $ \loc -> unless (isJustThisRepo && not (isThisRepo loc)) $ do let isWritable = writable loc@@ -174,7 +155,7 @@ warn <- readIORef warnRelatedRepos when (warn && not (isThisRepo loc)) $ do writeIORef warnRelatedRepos False- putInfo $+ putInfo opts $ text $ "Also checking related repos and caches; use " ++ "--just-this-repo to disable.\n" ++ "Checking " ++ dir@@ -194,12 +175,12 @@ when isCorrupt $ do -- Count of files in current directory tell (Sum 1)- liftIO . putVerbose $+ liftIO . putVerbose opts $ text $ "Corrupt: " ++ file when (isWritable && shouldRepair) $ doRepair file uncompressed when (count > (0 :: Int)) $ do- liftIO . putInfo $+ liftIO . putInfo opts $ text $ "Found " ++ show count ++ " corrupt file" ++ (if count > 1 then "s" else "") ++ (if shouldRepair@@ -209,12 +190,9 @@ else "") -- Something corrupt somewhere tell (Any True)- when (Check `elem` opts && checkFailed) $+ when (action == O.GzcrcsCheck && checkFailed) $ exitWith (ExitFailure 1) where- [shouldRepair, isQuiet, isVerbose, isJustThisRepo] = zipWith ($)- (elem `fmap` [Repair, Quiet, Verbose, JustThisRepo]) (repeat opts)- putInfo = unless isQuiet . putDocLn . text- putVerbose = when isVerbose . putDocLn . text+ shouldRepair = action == O.GzcrcsRepair+ isJustThisRepo = O.justThisRepo ? opts doRepair name contents = liftIO $ gzWriteAtomicFilePSs name contents-
@@ -34,27 +34,31 @@ ( CommandArgs(..) , CommandControl(..) , normalCommand- , DarcsCommand(..), withStdOpts+ , DarcsCommand(..) , WrappedCommand(..) , wrappedCommandName , disambiguateCommands , extractCommands- , getCommandHelp+ , getSubcommands , nodefaults- , usage ) import Darcs.UI.External ( viewDoc )+import Darcs.UI.Usage+ ( getCommandHelp+ , usage+ , subusage+ ) import Darcs.Util.Lock ( environmentHelpTmpdir, environmentHelpKeepTmpdir , environmentHelpLocks ) import Darcs.Patch.Match ( helpOnMatchers )-import Darcs.Repository.Prefs ( boringFileHelp, binariesFileHelp, environmentHelpHome )+import Darcs.Repository.Prefs ( environmentHelpHome, prefsFilesHelp ) import Darcs.Util.Ssh ( environmentHelpSsh, environmentHelpScp, environmentHelpSshPort ) import Darcs.Util.File ( withCurrentDirectory ) import Darcs.Util.Path ( AbsolutePath ) import Control.Arrow ( (***) ) import Data.Char ( isAlphaNum, toLower, toUpper ) import Data.Either ( partitionEithers )-import Data.List ( groupBy, isPrefixOf, intercalate, nub )+import Data.List ( groupBy, isPrefixOf, intercalate, nub, lookup ) import Darcs.Util.English ( andClauses ) import Darcs.Util.Printer (text, vcat, vsep, ($$), empty) import Darcs.Util.Printer.Color ( environmentHelpColor, environmentHelpEscape, environmentHelpEscapeWhite )@@ -62,8 +66,7 @@ import Version ( version ) import Darcs.Util.Download ( environmentHelpProxy, environmentHelpProxyPassword ) import Darcs.Util.Workaround ( getCurrentDirectory )-import Darcs.UI.Options ( DarcsOption, defaultFlags, ocheck, onormalise, oid )-import qualified Darcs.UI.Options.All as O ( StdCmdAction, Verbosity, UseCache )+import Darcs.UI.Options ( defaultFlags, ocheck, onormalise, oid ) import qualified Darcs.UI.TheCommands as TheCommands helpDescription :: String@@ -75,22 +78,25 @@ "commands and a short description of each one. With an extra argument,\n" ++ "`darcs help foo` prints detailed help about the darcs command foo.\n" -argPossibilities :: [String]-argPossibilities = map wrappedCommandName $ extractCommands commandControlList+-- | Starting from a list of 'CommandControl's, unwrap one level+-- to get a list of command names together with their subcommands.+unwrapTree :: [CommandControl] -> [(String, [CommandControl])]+unwrapTree cs = [ (wrappedCommandName c, subcmds c) | CommandData c <- cs ]+ where+ subcmds (WrappedCommand sc) = getSubcommands sc -helpOpts :: DarcsOption a- (Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-helpOpts = withStdOpts oid oid+-- | Given a list of (normal) arguments to the help command, produce a list+-- of possible completions for the next (normal) argument.+completeArgs :: [String] -> [String]+completeArgs [] = map fst (unwrapTree commandControlList) ++ extraArgs where+ extraArgs = [ "manpage", "markdown", "patterns", "environment" ]+completeArgs (arg:args) = exploreTree arg args commandControlList where+ exploreTree cmd cmds cs =+ case lookup cmd (unwrapTree cs) of+ Nothing -> []+ Just cs' -> case cmds of+ [] -> map fst (unwrapTree cs')+ sub:cmds' -> exploreTree sub cmds' cs' help :: DarcsCommand [DarcsFlag] help = DarcsCommand@@ -102,13 +108,13 @@ , commandExtraArgHelp = ["[<DARCS_COMMAND> [DARCS_SUBCOMMAND]] "] , commandCommand = \ x y z -> helpCmd x y z >> exitSuccess , commandPrereq = \_ -> return $ Right ()- , commandGetArgPossibilities = return argPossibilities+ , commandCompleteArgs = \_ _ -> return . completeArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = []- , commandDefaults = defaultFlags helpOpts- , commandCheckOptions = ocheck helpOpts- , commandParseOptions = onormalise helpOpts+ , commandDefaults = defaultFlags oid+ , commandCheckOptions = ocheck oid+ , commandParseOptions = onormalise oid } helpCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()@@ -143,18 +149,21 @@ [] -> Left v es -> Right es -helpCmd _ _ [] = viewDoc $ text $ usage commandControlList+helpCmd _ _ [] = viewDoc $ usage commandControlList helpCmd _ _ (cmd:args) =- let disambiguated = disambiguateCommands commandControlList cmd args- in case disambiguated of+ case disambiguateCommands commandControlList cmd args of Left err -> fail err- Right (cmds,_) ->+ Right (cmds,as) -> let msg = case cmds of CommandOnly c -> getCommandHelp Nothing c- SuperCommandOnly c -> getCommandHelp Nothing c+ SuperCommandOnly c ->+ if null as then+ getCommandHelp Nothing c+ else+ text $ "Invalid subcommand!\n\n" ++ subusage c SuperCommandSub c s -> getCommandHelp (Just c) s- in viewDoc $ text msg+ in viewDoc $ msg listAvailableCommands :: IO () listAvailableCommands =@@ -168,7 +177,6 @@ putStrLn "--help" putStrLn "--version" putStrLn "--exact-version"- putStrLn "--overview" where isRight (Right _) = True isRight _ = False @@ -263,10 +271,7 @@ unlines commands, unlines environment, ".SH FILES",- ".SS \"_darcs/prefs/binaries\"",- escape $ unlines binariesFileHelp,- ".SS \"_darcs/prefs/boring\"",- escape $ unlines boringFileHelp,+ unlines prefFiles, ".SH BUGS", "At http://bugs.darcs.net/ you can find a list of known", "bugs in Darcs. Unknown bugs can be reported at that",@@ -357,15 +362,23 @@ then repl ++ replace find repl (drop (length find) s) else head s : replace find repl (tail s) + prefFiles = concatMap go prefsFilesHelp+ where go (f,h) = [".SS \"_darcs/prefs/" ++ f ++ "\"", escape h]+ markdownLines :: [String] markdownLines = [ "# Commands", "" , unlines commands- , "# Environment variables"- , "", unlines environment , "# Patterns"- , "", unlines helpOnMatchers ]+ , "", unlines helpOnMatchers+ , "# Configuration"+ , "", unlines prefFiles+ , "# Environment variables"+ , "", unlines environment ] where+ prefFiles = concatMap go prefsFilesHelp+ where go (f,h) = ["## `_darcs/prefs/" ++ f ++ "`", "", h]+ environment :: [String] environment = intercalate [""] [ renderEnv ks ds | (ks, ds) <- environmentHelp ]@@ -395,12 +408,12 @@ where opts2 = commandAdvancedOptions c environmentHelpEditor :: ([String], [String])-environmentHelpEditor = (["DARCS_EDITOR", "DARCSEDITOR", "VISUAL", "EDITOR"],[+environmentHelpEditor = (["DARCS_EDITOR", "VISUAL", "EDITOR"],[ "To edit a patch description of email comment, Darcs will invoke an", "external editor. Your preferred editor can be set as any of the",- "environment variables $DARCS_EDITOR, $DARCSEDITOR, $VISUAL or $EDITOR.",- "If none of these are set, vi(1) is used. If vi crashes or is not",- "found in your PATH, emacs, emacs -nw, nano and (on Windows) edit are",+ "environment variables $DARCS_EDITOR, $VISUAL or $EDITOR.",+ "If none of these are set, nano is used. If nano crashes or is not",+ "found in your PATH, vi, emacs, emacs -nw and (on Windows) edit are", "each tried in turn."]) environmentHelpPager :: ([String], [String])@@ -412,8 +425,8 @@ environmentHelpTimeout :: ([String], [String]) environmentHelpTimeout = (["DARCS_CONNECTION_TIMEOUT"],[ "Set the maximum time in seconds that darcs allows and connection to",- "take. If the variable is not specified the default are 30 seconds. This",- "option only works with curl."])+ "take. If the variable is not specified the default are 30 seconds.",+ "This option only works with curl."]) -- | There are two environment variables that we do not document: -- - DARCS_USE_ISPRINT: deprecated, use DARCS_DONT_ESCAPE_ISPRINT.
@@ -23,18 +23,15 @@ import Prelude hiding ( (^) ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amNotInRepository, putInfo )-import Darcs.UI.Flags ( DarcsFlag( WorkRepoDir ), withWorkingDir, patchFormat, runPatchIndex )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Completion ( noArgs )+import Darcs.UI.Flags ( DarcsFlag( WorkRepoDir ) )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O- ( patchFormat, useWorkingDir, workingRepoDir, patchIndex, hashed- , PatchFormat, WithWorkingDir, WithPatchIndex- , StdCmdAction, Verbosity, UseCache, PatchFormat(..)- ) import Darcs.UI.Options.All ( ) import Darcs.Util.Printer ( text ) import Darcs.Util.Path ( AbsolutePath ) import Darcs.Util.Text ( quote )-import Darcs.Repository ( createRepository )+import Darcs.Repository ( createRepository, withUMaskFlag ) initializeDescription :: String initializeDescription = "Create an empty repository."@@ -55,31 +52,6 @@ "\n" ++ "Initialize is commonly abbreviated to `init`.\n" -initBasicOpts :: DarcsOption a (O.PatchFormat -> O.WithWorkingDir -> Maybe String -> a)-initBasicOpts = O.patchFormat ^ O.useWorkingDir ^ O.workingRepoDir--initAdvancedOpts :: DarcsOption a (O.WithPatchIndex -> () -> a)-initAdvancedOpts = O.patchIndex ^ O.hashed--initOpts :: DarcsOption a- (O.PatchFormat- -> O.WithWorkingDir- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.WithPatchIndex- -> ()- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-initOpts = initBasicOpts `withStdOpts` initAdvancedOpts- initialize :: DarcsCommand [DarcsFlag] initialize = DarcsCommand { commandProgramName = "darcs"@@ -90,7 +62,7 @@ , commandExtraArgHelp = ["[<DIRECTORY>]"] , commandPrereq = \_ -> return $ Right () , commandCommand = initializeCmd- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc initAdvancedOpts , commandBasicOptions = odesc initBasicOpts@@ -98,16 +70,25 @@ , commandCheckOptions = ocheck initOpts , commandParseOptions = onormalise initOpts }+ where+ initBasicOpts = O.patchFormat ^ O.withWorkingDir ^ O.repoDir+ initAdvancedOpts = O.patchIndexNo ^ O.hashed+ initOpts = initBasicOpts `withStdOpts` initAdvancedOpts initializeCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () initializeCmd aps opts [outname] | null [ () | WorkRepoDir _ <- opts ] = initializeCmd aps (WorkRepoDir outname:opts) []-initializeCmd _ opts [] = do- location <- amNotInRepository opts- case location of- Left msg -> fail $ "Unable to " ++ quote ("darcs " ++ commandName initialize)- ++ " here.\n\n" ++ msg- Right () -> do- createRepository (patchFormat opts) (withWorkingDir opts) (runPatchIndex opts)- putInfo opts $ text "Repository initialized."+initializeCmd _ opts [] =+ withUMaskFlag (O.umask ? opts) $ do+ location <- amNotInRepository opts+ case location of+ Left msg -> fail $ "Unable to " ++ quote ("darcs " ++ commandName initialize)+ ++ " here.\n\n" ++ msg+ Right () -> do+ _ <- createRepository+ (O.patchFormat ? opts)+ (O.withWorkingDir ? opts)+ (O.patchIndexNo ? opts)+ (O.useCache ? opts)+ putInfo opts $ text "Repository initialized." initializeCmd _ _ _ = fail "You must provide 'initialize' with either zero or one argument."
@@ -15,8 +15,7 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, PatternGuards #-}-+{-# LANGUAGE PatternGuards #-} module Darcs.UI.Commands.Log ( changes, log@@ -26,7 +25,6 @@ import Prelude () import Darcs.Prelude -import Unsafe.Coerce (unsafeCoerce) import Data.List ( intersect, sort, nub, find ) import Data.Maybe ( fromMaybe, fromJust, isJust ) import Control.Arrow ( second )@@ -36,58 +34,57 @@ import Darcs.UI.PrintPatch ( showFriendly ) import Darcs.Patch.PatchInfoAnd ( fmapFLPIAP, hopefullyM, info ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, commandAlias, findRepository )+import Darcs.UI.Completion ( knownFileArgs ) import Darcs.UI.External ( viewDocWith ) import Darcs.UI.Flags- ( DarcsFlag(GenContext,- MachineReadable, Count, Interactive,- NumberPatches, XMLOutput, Summary,- Verbose, Debug, NoPatchIndexFlag)- , doReverse, showChangesOnlyToFiles- , useCache, maxCount, umask- , verbosity, isUnified, isInteractive, hasSummary+ ( DarcsFlag+ , changesReverse, onlyToFiles+ , useCache, maxCount, hasXmlOutput+ , verbosity, withContext, isInteractive, verbose , fixSubPaths, getRepourl )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Util.Path ( SubPath(), toFilePath, fp2fn, fn2fp, normPath, AbsolutePath, simpleSubPath ) import Darcs.Repository ( PatchSet, PatchInfoAnd,- withRepositoryDirectory, RepoJob(..),+ withRepositoryLocation, RepoJob(..), readRepo, unrecordedChanges, withRepoLockCanFail )-import Darcs.Repository.Flags ( UseIndex(..), ScanKnown(..), DiffAlgorithm(MyersDiff), UpdateWorking(..) )+import Darcs.Repository.Flags ( UseIndex(..), ScanKnown(..), DiffAlgorithm(MyersDiff) ) import Darcs.Util.Lock ( withTempDir )-import Darcs.Patch.Set ( PatchSet(..), newset2RL )+import Darcs.Patch.Set ( PatchSet(..), patchSet2RL ) import Darcs.Patch.Conflict ( Conflict, CommuteNoConflicts ) import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.FileHunk ( IsHunk )-import Darcs.Patch.Info ( toXml, showPatchInfo, escapeXML, PatchInfo )+import Darcs.Patch.Info ( toXml, toXmlShort, showPatchInfo, displayPatchInfo, escapeXML, PatchInfo ) import Darcs.Patch.Depends ( findCommonWithThem ) import Darcs.Patch.Bundle( contextPatches ) import Darcs.Patch.Prim ( PrimPatchBase )-import Darcs.Patch.Show ( ShowPatch )+import Darcs.Patch.Show ( ShowPatch, ShowPatchFor(..) ) import Darcs.Patch.TouchesFiles ( lookTouch ) import Darcs.Patch.Type ( PatchType(PatchType) ) import Darcs.Patch.Apply ( Apply, ApplyState ) import Darcs.Patch ( IsRepoType, invert, xmlSummary, description, effectOnFilePaths, listTouchedFiles, showPatch )-import Darcs.Patch.Named.Wrapped ( (:~:)(..) )+import Darcs.Patch.Named.Wrapped ( (:~:)(..), getdeps ) import Darcs.Patch.Witnesses.Eq ( EqCheck(..) ) import Darcs.Patch.Witnesses.Ordered ( FL(NilFL), RL(..), filterOutFLFL, filterRL, reverseFL, (:>)(..), mapRL ) import Darcs.Patch.Witnesses.Sealed ( Sealed2(..), unseal2, Sealed(..), seal2 )+import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) import Darcs.Patch.Match ( MatchFlag , firstMatch , secondMatch- , matchAPatchread+ , matchAPatch , haveNonrangeMatch , matchFirstPatchset , matchSecondPatchset ) import Darcs.Patch.Matchable ( Matchable ) import Darcs.Util.Printer ( Doc, simplePrinters, (<+>), prefix, text, vcat,- vsep, (<>), ($$), errorDoc, insertBeforeLastline, empty, RenderMode(..) )+ vsep, (<>), ($$), errorDoc, insertBeforeLastline, empty ) import Darcs.Util.Printer.Color ( fancyPrinters ) import Darcs.Util.Progress ( setProgressMode, debugMessage ) import Darcs.Util.URL ( isValidLocalPath )@@ -105,56 +102,6 @@ ++ logHelp' ++ logHelp'' -logBasicOpts :: DarcsOption a- ([O.MatchFlag]- -> Maybe Int- -> Bool- -> Maybe O.ChangesFormat- -> Maybe O.Summary- -> Bool- -> Maybe String- -> Maybe String- -> Maybe Bool- -> a)-logBasicOpts- = O.matchSeveralOrRange- ^ O.matchMaxcount- ^ O.onlyToFiles- ^ O.changesFormat- ^ O.summary- ^ O.changesReverse- ^ O.possiblyRemoteRepo- ^ O.workingRepoDir- ^ O.interactive -- False--logAdvancedOpts :: DarcsOption a (O.NetworkOptions -> O.WithPatchIndex -> a)-logAdvancedOpts = O.network ^ O.patchIndexYes--logOpts :: DarcsOption a- ([O.MatchFlag]- -> Maybe Int- -> Bool- -> Maybe O.ChangesFormat- -> Maybe O.Summary- -> Bool- -> Maybe String- -> Maybe String- -> Maybe Bool- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.NetworkOptions- -> O.WithPatchIndex- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-logOpts = logBasicOpts `withStdOpts` logAdvancedOpts- log :: DarcsCommand [DarcsFlag] log = DarcsCommand { commandProgramName = "darcs"@@ -163,7 +110,7 @@ , commandDescription = "List patches in the repository." , commandExtraArgs = -1 , commandExtraArgHelp = ["[FILE or DIRECTORY]..."]- , commandGetArgPossibilities = return []+ , commandCompleteArgs = knownFileArgs , commandCommand = logCmd , commandPrereq = findRepository , commandArgdefaults = nodefaults@@ -173,10 +120,23 @@ , commandCheckOptions = ocheck logOpts , commandParseOptions = onormalise logOpts }+ where+ logBasicOpts+ = O.matchSeveralOrRange+ ^ O.maxCount+ ^ O.onlyToFiles+ ^ O.changesFormat+ ^ O.summary+ ^ O.changesReverse+ ^ O.possiblyRemoteRepo+ ^ O.repoDir+ ^ O.interactive+ logAdvancedOpts = O.network ^ O.patchIndexYes+ logOpts = logBasicOpts `withStdOpts` logAdvancedOpts logCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () logCmd fps opts args- | GenContext `elem` opts = if not . null $ args+ | O.changesFormat ? opts == Just O.GenContext = if not . null $ args then fail "log --context cannot accept other arguments" else logContext opts | hasRemoteRepo opts = do@@ -191,10 +151,10 @@ fs <- fixSubPaths fps args case fs of [] -> putStrLn "No valid arguments were given, nothing to do."- _ -> do unless (Interactive `elem` opts)- $ unless (NoPatchIndexFlag `elem` opts)- $ withRepoLockCanFail (useCache opts) YesUpdateWorking (umask opts)- $ RepoJob attemptCreatePatchIndex+ _ -> do unless (isInteractive False opts)+ $ when (O.patchIndexNo ? opts == O.YesPatchIndex)+ $ withRepoLockCanFail (useCache ? opts)+ $ RepoJob (\repo -> readRepo repo >>= attemptCreatePatchIndex repo) showLog opts $ Just $ nub $ sort fs maybeNotNull :: [a] -> Maybe [a]@@ -215,41 +175,43 @@ showLog :: [DarcsFlag] -> Maybe [SubPath] -> IO () showLog opts files = let repodir = fromMaybe "." (getRepourl opts) in- withRepositoryDirectory (useCache opts) repodir $ RepoJob $ \repository -> do- unless (Debug `elem` opts) $ setProgressMode False+ withRepositoryLocation (useCache ? opts) repodir $ RepoJob $ \repository -> do+ unless (O.debug ? opts) $ setProgressMode False Sealed unrec <- case files of Nothing -> return $ Sealed NilFL- Just _ -> Sealed `fmap` unrecordedChanges (UseIndex, ScanKnown, MyersDiff) repository files+ Just _ -> Sealed `fmap` unrecordedChanges (UseIndex, ScanKnown, MyersDiff)+ O.NoLookForMoves O.NoLookForReplaces+ repository files `catch` \(_ :: IOException) -> return (Sealed NilFL) -- this is triggered when repository is remote+ debugMessage "About to read the repository..."+ patches <- readRepo repository+ debugMessage "Done reading the repository." let normfp = fn2fp . normPath . fp2fn undoUnrecordedOnFPs = effectOnFilePaths (invert unrec) recFiles = map normfp . undoUnrecordedOnFPs . map toFilePath <$> files filtered_changes p = maybe_reverse <$> getLogInfo- (maxCount opts)+ (maxCount ? opts) (parseFlags O.matchSeveralOrRange opts)- (showChangesOnlyToFiles opts)+ (onlyToFiles ? opts) recFiles- (maybeFilterPatches repository)+ (maybeFilterPatches repository patches) p- debugMessage "About to read the repository..."- patches <- readRepo repository- debugMessage "Done reading the repository."- if Interactive `elem` opts+ if isInteractive False opts then do (fp_and_fs, _, _) <- filtered_changes patches let fp = map fst fp_and_fs viewChanges (logPatchSelOpts opts) fp- else do let header = if isJust files && XMLOutput `notElem` opts+ else do let header = if isJust files && hasXmlOutput opts then text $ "Changes to "++unwords (fromJust recFiles)++":\n" else empty debugMessage "About to print the patches..."- let printers = if XMLOutput `elem` opts then simplePrinters else fancyPrinters+ let printers = if hasXmlOutput opts then simplePrinters else fancyPrinters ps <- readRepo repository -- read repo again to prevent holding onto -- values forced by filtered_changes logOutput <- changelog opts ps `fmap` filtered_changes patches- viewDocWith printers Encode $ header $$ logOutput- where maybe_reverse (xs,b,c) = if doReverse opts+ viewDocWith printers $ header $$ logOutput+ where maybe_reverse (xs,b,c) = if changesReverse ? opts then (reverse xs, b, c) else (xs, b, c) @@ -302,7 +264,7 @@ then matchSecondPatchset matchFlags ps else Sealed ps pf = if haveNonrangeMatch (PatchType :: PatchType rt p) matchFlags- then matchAPatchread matchFlags+ then matchAPatch matchFlags else \_ -> True filterOutUnrelatedChanges (pfs, renames, doc)@@ -315,7 +277,7 @@ unrelated fs p -- If the change does not affect the patches we are looking at, -- we ignore the difference between the two states.- | null $ fs `intersect` listTouchedFiles p = unsafeCoerce IsEq+ | null $ fs `intersect` listTouchedFiles p = unsafeCoerceP IsEq | otherwise = NotEq -- | Take a list of filenames and patches and produce a list of patches that@@ -350,7 +312,7 @@ else case hopefullyM hp of Nothing -> do let err = text "Can't find patches prior to:"- $$ showPatchInfo (info hp)+ $$ displayPatchInfo (info hp) return ([], renames, Just err) Just p -> case lookTouch (Just renames) fs (invert p) of@@ -382,39 +344,48 @@ -> ([(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- | XMLOutput `elem` opts =+ | O.changesFormat ? opts == Just O.CountPatches = text $ show $ length pis_and_fs+ | hasXmlOutput opts = text "<changelog>" $$ vcat created_as_xml $$ vcat actual_xml_changes $$ text "</changelog>"- | Summary `elem` opts || Verbose `elem` opts =+ | O.yes (O.summary ? opts) || verbose opts = 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 rt p) -> Doc change_with_summary (Sealed2 hp)- | 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]")+ | Just p <- hopefullyM hp =+ if O.changesFormat ? opts == Just O.MachineReadable+ then showPatch ForStorage p+ else showFriendly (verbosity ? opts) (O.summary ? opts) p+ | otherwise = description hp $$ indent (text "[this patch is unavailable]") - xml_with_summary (Sealed2 hp)- | Just p <- hopefullyM hp = insertBeforeLastline- (toXml $ info hp) (indent $ xmlSummary p)+ xml_with_summary (Sealed2 hp) | Just p <- hopefullyM hp =+ let+ deps = getdeps p+ xmlDependencies =+ text "<explicit_dependencies>"+ $$ vcat (map (indent . toXmlShort) deps)+ $$ text "</explicit_dependencies>"+ summary | deps == [] = indent $ xmlSummary p+ | otherwise = indent $ xmlDependencies $$ xmlSummary p+ in+ insertBeforeLastline (toXml $ info hp) summary xml_with_summary (Sealed2 hp) = toXml (info hp) indent = prefix " "- actual_xml_changes = if Summary `elem` opts- then map xml_with_summary pis- else map (toXml . unseal2 info) pis+ actual_xml_changes =+ case O.summary ? opts of+ O.YesSummary -> map xml_with_summary pis+ O.NoSummary -> map (toXml . unseal2 info) pis created_as_xml = map create createdAsFs where create rename@(_, as) = createdAsXml (first_change_of as) rename -- We need to reorder the patches when they haven't been reversed -- already, so that we find the *first* patch that modifies a given -- file, not the last (by default, the list is oldest->newest).- reorderer = if not (doReverse opts) then reverse else id+ reorderer = if not (changesReverse ? opts) then reverse else id oldest_first_pis_and_fs = reorderer pis_and_fs couldnt_find fn = error $ "Couldn't find first patch affecting " ++ fn ++ " in pis_and_fs"@@ -422,13 +393,13 @@ find_first_change_of fn = fromMaybe (couldnt_find fn) (mb_first_change_of fn) first_change_of = unseal2 info . fst . find_first_change_of- number_patch f x = if NumberPatches `elem` opts+ number_patch f x = if O.changesFormat ? opts == Just O.NumberPatches then case get_number x of Just n -> text (show n++":") <+> f x Nothing -> f x else f x get_number :: Sealed2 (PatchInfoAnd re p) -> Maybe Int- get_number (Sealed2 y) = gn 1 (newset2RL patchset)+ get_number (Sealed2 y) = gn 1 (patchSet2RL patchset) where iy = info y gn :: Int -> RL (PatchInfoAnd rt p) wStart wY -> Maybe Int gn n (bs:<:b) | seq n (info b) == iy = Just n@@ -452,12 +423,12 @@ logContext :: [DarcsFlag] -> IO () logContext opts = do let repodir = fromMaybe "." $ getRepourl opts- withRepositoryDirectory (useCache opts) repodir $ RepoJob $ \repository -> do+ withRepositoryLocation (useCache ? opts) repodir $ RepoJob $ \repository -> do (_ :> ps') <- contextPatches `fmap` readRepo repository 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+ let logOutput = maybe (vsep $ map (unseal2 (showPatchInfo ForStorage . info)) pis) errorDoc Nothing+ viewDocWith simplePrinters $ header $$ logOutput -- | changes is an alias for log changes :: DarcsCommand [DarcsFlag]@@ -475,10 +446,10 @@ logPatchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions logPatchSelOpts flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = parseFlags O.matchSeveralOrRange flags , S.interactive = isInteractive False flags , S.selectDeps = O.PromptDeps -- option not supported, use default- , S.summary = hasSummary O.NoSummary flags- , S.withContext = isUnified flags+ , S.summary = O.summary ? flags+ , S.withContext = withContext ? flags }
@@ -15,41 +15,54 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} module Darcs.UI.Commands.MarkConflicts ( markconflicts ) where import Prelude () import Darcs.Prelude -import Prelude hiding ( (^) )- import System.Exit ( exitSuccess )-import Data.List.Ordered ( nubSort )+import Data.List.Ordered ( nubSort, isect ) import Control.Monad ( when, unless ) import Control.Exception ( catch, IOException ) import Darcs.Util.Prompt ( promptYorn )-import Darcs.Util.Path ( AbsolutePath ) import Darcs.Util.SignalHandler ( withSignalsBlocked )-import Darcs.Util.Printer( putDocLn, putDocLnWith, text, redText, ($$) )-import Darcs.Util.Printer.Color (fancyPrinters)+import Darcs.Util.Path ( AbsolutePath, SubPath, toFilePath, simpleSubPath )+import Darcs.Util.Printer+ ( Doc, putDocLnWith, text, redText, debugDocLn, vsep, (<>), (<+>), ($$) )+import Darcs.Util.Printer.Color ( fancyPrinters )+import Darcs.Util.Text ( pathlist ) -import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )-import Darcs.UI.Flags ( DarcsFlag, diffingOpts, verbosity, dryRun, umask, useCache )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Commands+ ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository, putInfo )+import Darcs.UI.Commands.Util ( filterExistingPaths )+import Darcs.UI.Completion ( knownFileArgs )+import Darcs.UI.Flags+ ( DarcsFlag, diffingOpts, verbosity, dryRun, umask+ , useCache, fixSubPaths )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O+ import Darcs.Repository.Flags ( UpdateWorking (..) )-import Darcs.Repository ( withRepoLock, RepoJob(..), addToPending,- applyToWorking,- readRepo, unrecordedChanges, Repository- )-import Darcs.Patch ( invert, PrimOf, listTouchedFiles )-import Darcs.Patch.Witnesses.Ordered ( FL(..) )+import Darcs.Repository+ ( withRepoLock+ , RepoJob(..)+ , addToPending+ , applyToWorking+ , readRepo+ , unrecordedChanges )++import Darcs.Patch ( invert, listTouchedFiles, effectOnFilePaths )+import Darcs.Patch.Show+import Darcs.Patch.TouchesFiles ( chooseTouching )+import Darcs.Patch.Witnesses.Ordered ( FL(..), mapFL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed) ) import Darcs.Repository.Resolution ( patchsetConflictResolutions )-#include "impossible.h" +-- * The mark-conflicts command+ markconflictsDescription :: String markconflictsDescription = "Mark unresolved conflicts in working tree, for manual resolution."@@ -72,61 +85,25 @@ ,"However, you might revert or manually delete these markers without" ,"actually resolving the conflict. In this case, `darcs mark-conflicts`" ,"is useful to show where are the unresolved conflicts. It is also"- ,"useful if `darcs apply` is called with `--apply-conflicts`,"- ,"where conflicts aren't marked initially."+ ,"useful if `darcs apply` or `darcs pull` is called with"+ ,"`--allow-conflicts`, where conflicts aren't marked initially." ,"" ,"Unless you use the `--dry-run` flag, any unrecorded changes to the"- ,"working tree WILL be lost forever when you run this command!"+ ,"affected files WILL be lost forever when you run this command!" ,"You will be prompted for confirmation before this takes place." ] -markconflictsBasicOpts :: DarcsOption a- (O.UseIndex- -> Maybe String- -> O.DiffAlgorithm- -> O.DryRun- -> O.XmlOutput- -> a)-markconflictsBasicOpts- = O.useIndex- ^ O.workingRepoDir- ^ O.diffAlgorithm- ^ O.dryRunXml--markconflictsAdvancedOpts :: DarcsOption a (O.UMask -> a)-markconflictsAdvancedOpts = O.umask--markconflictsOpts :: DarcsOption a- (O.UseIndex- -> Maybe String- -> O.DiffAlgorithm- -> O.DryRun- -> O.XmlOutput- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-markconflictsOpts = markconflictsBasicOpts `withStdOpts` markconflictsAdvancedOpts- markconflicts :: DarcsCommand [DarcsFlag] markconflicts = DarcsCommand { commandProgramName = "darcs" , commandName = "mark-conflicts" , commandHelp = markconflictsHelp , commandDescription = markconflictsDescription- , commandExtraArgs = 0- , commandExtraArgHelp = []+ , commandExtraArgs = -1+ , commandExtraArgHelp = ["[FILE or DIRECTORY]..."] , commandCommand = markconflictsCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = knownFileArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc markconflictsAdvancedOpts , commandBasicOptions = odesc markconflictsBasicOpts@@ -134,33 +111,223 @@ , commandCheckOptions = ocheck markconflictsOpts , commandParseOptions = onormalise markconflictsOpts }+ where+ markconflictsBasicOpts+ = O.useIndex+ ^ O.repoDir+ ^ O.diffAlgorithm+ ^ O.dryRunXml+ markconflictsAdvancedOpts = O.umask+ markconflictsOpts = markconflictsBasicOpts `withStdOpts` markconflictsAdvancedOpts markconflictsCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-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- case nubSort $ listTouchedFiles res of- [] -> putStrLn "No conflicts to mark." >> exitSuccess- cfs -> putDocLnWith fancyPrinters $- redText "Conflicts found in the following files:" $$ text (unlines cfs)- when (dryRun opts == O.YesDryRun) $ do- putDocLn $ text "Conflicts will not be marked: this is a dry run."+markconflictsCmd fps opts args = do+ paths <- if null args then return Everything else sps2ps <$> fixSubPaths fps args -- Applicative IO+ debugDocLn $ "::: paths =" <+> (text . show) paths+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $ RepoJob $ \repository -> do++{-+ What we do here:+ * read the unrecorded changes (all of them)+ * extract functions representing path rename effects from unrecorded+ * convert argument paths to pre-pending+ * read conflict resolutions that touch pre-pending argument paths+ * affected paths = intersection of paths touched by resolutions+ and pre-pending argument paths+ * for these paths, revert pending changes+ * apply the (filtered, see above) conflict resolutions++ Technical side-note:+ Ghc can't handle pattern bindings for existentials. So 'let' is out,+ one has to use 'case expr of var ->' or 'do var <- return expr'.+ Case is clearer but do-notation does not increase indentation depth.+ So we use case for small-scope bindings and <-/return when the scope+ is a long do block.+-}++ let (useidx, scan, _) = diffingOpts opts+ verb = verbosity ? opts+ classified_paths <-+ traverse (filterExistingPaths repository verb useidx scan O.NoLookForMoves) paths++ unrecorded <- unrecordedChanges (diffingOpts opts)+ O.NoLookForMoves O.NoLookForReplaces+ repository (fromOnly Everything)++ let forward_renames = liftToPathSet (effectOnFilePaths unrecorded)+ backward_renames = liftToPathSet (effectOnFilePaths (invert unrecorded))+ existing_paths = fmap snd classified_paths+ pre_pending_paths = backward_renames existing_paths+ debugDocLn $ "::: pre_pending_paths =" <+> (text . show) pre_pending_paths++ r <- readRepo repository+ Sealed res <- case patchsetConflictResolutions r of+ Sealed raw_res -> do+ let raw_res_paths = fps2ps (listTouchedFiles raw_res)+ debugDocLn $ "::: raw_res_paths =" <+> (text . show) raw_res_paths+ return $ chooseTouching (ps2fps pre_pending_paths) raw_res+ let res_paths = fps2ps (listTouchedFiles res)+ debugDocLn $ "::: res_paths =" <+> (text . show) res_paths++ let affected_paths = isectPathSet res_paths pre_pending_paths+ debugDocLn $ "::: affected_paths =" <+> (text . show) affected_paths++ when (affected_paths == Only []) $ do+ putInfo opts "No conflicts to mark." exitSuccess- 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"++- " in the working directory.")- confirmed <- promptYorn "Are you sure? "- unless confirmed exitSuccess- applyToWorking repository (verbosity opts) (invert pend') `catch` \(e :: IOException) ->- bug ("Can't undo pending changes!" ++ show e)- repository' <- undoUnrec pend- withSignalsBlocked $- do addToPending repository' YesUpdateWorking res- _ <- applyToWorking repository' (verbosity opts) res `catch` \(e :: IOException) ->- bug ("Problem marking conflicts in mark-conflicts!" ++ show e)- return ()- putStrLn "Finished marking conflicts."-markconflictsCmd _ _ _ = impossible++ to_revert <- unrecordedChanges (diffingOpts opts)+ O.NoLookForMoves O.NoLookForReplaces+ repository (fromOnly affected_paths)++ let post_pending_affected_paths = forward_renames affected_paths+ putInfo opts $ "Marking conflicts in:" <+> showPathSet post_pending_affected_paths <> "."++ debugDocLn $ "::: to_revert =" $$ vsep (mapFL displayPatch to_revert)+ debugDocLn $ "::: res = " $$ vsep (mapFL displayPatch res)+ when (O.yes (dryRun ? opts)) $ do+ putInfo opts $ "Conflicts will not be marked: this is a dry run."+ exitSuccess++ repository' <- case to_revert of+ NilFL -> return repository+ _ -> do+ -- TODO:+ -- (1) create backups for all files where we revert changes+ -- (2) try to add the reverted stuff to the unrevert bundle+ -- after (1) and (2) is done we can soften the warning below+ putDocLnWith fancyPrinters $+ "Warning: This will revert all unrecorded changes in:"+ <+> showPathSet post_pending_affected_paths <> "."+ $$ redText "These changes will be LOST."+ confirmed <- promptYorn "Are you sure? "+ unless confirmed exitSuccess++{- -- copied from Revert.hs, see comment (2) above+ 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+ debugMessage "About to apply to the working directory."+-}++ let to_add = invert to_revert+ addToPending repository YesUpdateWorking to_add+ applyToWorking repository (verbosity ? opts) to_add `catch` \(e :: IOException) ->+ bug ("Can't undo pending changes!" ++ show e)+ withSignalsBlocked $+ do addToPending repository' YesUpdateWorking res+ _ <- applyToWorking repository' (verbosity ? opts) res `catch` \(e :: IOException) ->+ bug ("Problem marking conflicts in mark-conflicts!" ++ show e)+ return ()+ putInfo opts "Finished marking conflicts."++-- * Generic 'PathSet' support++{- $SupportCode++What follows is generic support code for working with argument path lists+that are used to restrict operations to a subset of the working or pristine+tree. The rest of Darcs uses two types for this:++ * @'Maybe' ['SubPath']@++ * @'Maybe' ['FilePath']@++The problem with both is the contra-intuitive name 'Nothing', which here+stands for 'Everything'. To make the intended use clearer, we use the 'Only'+type instead (which is is isomorphic to 'Maybe') and the synonym 'PathSet'+defined below.++These abstractions should get their own module (or become integrated into+Darcs.Util.Path) if and when someone decides to reuse it elsewhere. The+functionality provided is intentionally minimal and light-weight.+-}++-- | 'Only' is isomorphic to 'Maybe' but with the opposite semantics.+--+-- About the name: I like the data constructor names, they are pretty+-- suggestive. The data type name is up for grabs; a possible alternative+-- is @AtMost@.+data Only a = Everything | Only a deriving (Eq, Ord, Show)++instance Functor Only where+ fmap _ Everything = Everything+ fmap f (Only x) = Only (f x)++instance Foldable Only where+ foldMap _ Everything = mempty+ foldMap f (Only x) = f x++instance Traversable Only where+ traverse _ Everything = pure Everything+ traverse f (Only x) = Only <$> f x++-- | This is mostly for conversion to legacy APIs+fromOnly :: Only a -> Maybe a+fromOnly Everything = Nothing+fromOnly (Only x) = Just x++{- | A set of repository paths. 'Everything' means every path in the repo, it+usually originates from an empty list of path arguments. The list of+'SubPath's is always kept in sorted order with no duplicates and normalised+(as in 'FilePath.normalise'). This has the nice effect of getting rid of the+idiotic "./" that Darcs insists on prepending to repo paths (which can make+things like comparing paths returned from different parts of the code base a+nightmare).++It uses 'SubPath' for easier compatibility and lists because the number of+elements is expected to be small.+-}+type PathSet = Only [SubPath]++-- | Intersection of two 'PathSet's+isectPathSet :: PathSet -> PathSet -> PathSet+isectPathSet Everything ys = ys+isectPathSet xs Everything = xs+isectPathSet (Only xs) (Only ys) = Only (isect xs ys)++{-+-- | Union of two 'PathSet's+union :: PathSet -> PathSet -> PathSet+union Everything ys = Everything+union xs Everything = Everything+union (Only xs) (Only ys) = Only (union xs ys)+-}++-- | Convert a list of 'SubPath's to a 'PathSet'.+sps2ps :: [SubPath] -> PathSet+sps2ps = Only . nubSort++-- | Convert a list of repo paths to a 'PathSet'.+-- Partial function! Use only with repo paths.+fps2ps :: [FilePath] -> PathSet+fps2ps = sps2ps . map fp2sp++-- | Convert a 'PathSet' to something that e.g. 'chooseTouching'+-- takes as parameter.+ps2fps :: PathSet -> Maybe [FilePath]+ps2fps = fmap (map sp2fp) . fromOnly++-- | Convert a 'PathSet' to a 'Doc'. Uses the English module+-- to generate a nicely readable list of file names.+showPathSet :: Only [SubPath] -> Doc+showPathSet Everything = text "all paths"+showPathSet (Only xs) = pathlist (map sp2fp xs)++-- | Lift a function transforming a list of 'FilePath' to one that+-- transforms a 'PathSet'.+liftToPathSet :: ([FilePath] -> [FilePath]) -> PathSet -> PathSet+liftToPathSet f = fmap (nubSort . map fp2sp . f . map sp2fp)++-- | Convert a 'FilePath' to a 'SubPath'.+--+-- Note: Should call this only with paths we get from the repository.+-- This guarantees that they are relative (to the repo dir).+fp2sp :: FilePath -> SubPath+fp2sp = fromJust . simpleSubPath++-- | Convert a 'SubPath' to a 'FilePath'. Same as 'toFilePath' and+-- only here for symmetry.+sp2fp :: SubPath -> FilePath+sp2fp = toFilePath
@@ -16,8 +16,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.UI.Commands.Move ( move, mv ) where import Prelude ()@@ -28,11 +26,17 @@ import Darcs.Util.SignalHandler ( withSignalsBlocked ) import Darcs.UI.Commands- ( DarcsCommand(..), withStdOpts, nodefaults, commandAlias, amInHashedRepository )-import Darcs.UI.Flags ( DarcsFlag(Quiet)- , doAllowCaseOnly, doAllowWindowsReserved, useCache, dryRun, umask- , maybeFixSubPaths, fixSubPaths )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags )+ ( DarcsCommand(..), withStdOpts, nodefaults, commandAlias, amInHashedRepository+ , putInfo+ )+import Darcs.UI.Completion ( knownFileArgs )+import Darcs.UI.Flags+ ( DarcsFlag+ , allowCaseDifferingFilenames, allowWindowsReservedFilenames+ , useCache, dryRun, umask+ , maybeFixSubPaths, fixSubPaths+ )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.Diff ( treeDiff ) import Darcs.Repository.Flags ( UpdateWorking (..), DiffAlgorithm(..) )@@ -45,13 +49,12 @@ , withRepoLock , RepoJob(..) , addPendingDiffToPending- , listFiles ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (+>+) ) import Darcs.Patch.Witnesses.Sealed ( emptyGap, freeGap, joinGap, FreeLeft ) import Darcs.Util.Global ( debugMessage ) import qualified Darcs.Patch-import Darcs.Patch ( RepoPatch, PrimPatch, PrimOf )+import Darcs.Patch ( RepoPatch, PrimPatch ) import Darcs.Patch.Apply( ApplyState ) import Data.List ( nub, sort ) import qualified System.FilePath.Windows as WindowsFilePath@@ -68,6 +71,7 @@ , toFilePath , AbsolutePath )+import Darcs.Util.Printer ( text, hsep ) import Darcs.Util.Workaround ( renameFile ) moveDescription :: String@@ -89,30 +93,6 @@ "`ReadMe` and `README`). If `--case-ok` is used, the repository might be\n" ++ "unusable on those systems!\n" -moveBasicOpts :: DarcsOption a (Bool -> Bool -> Maybe String -> a)-moveBasicOpts = O.allowProblematicFilenames ^ O.workingRepoDir--moveAdvancedOpts :: DarcsOption a (O.UMask -> a)-moveAdvancedOpts = O.umask--moveOpts :: DarcsOption a- (Bool- -> Bool- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-moveOpts = moveBasicOpts `withStdOpts` moveAdvancedOpts- move :: DarcsCommand [DarcsFlag] move = DarcsCommand { commandProgramName = "darcs"@@ -123,7 +103,7 @@ , commandExtraArgHelp = ["<SOURCE> ... <DESTINATION>"] , commandCommand = moveCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = listFiles False+ , commandCompleteArgs = knownFileArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc moveAdvancedOpts , commandBasicOptions = odesc moveBasicOpts@@ -131,6 +111,10 @@ , commandCheckOptions = ocheck moveOpts , commandParseOptions = onormalise moveOpts }+ where+ moveBasicOpts = O.allowProblematicFilenames ^ O.repoDir+ moveAdvancedOpts = O.umask+ moveOpts = moveBasicOpts `withStdOpts` moveAdvancedOpts moveCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () moveCmd fps opts args@@ -235,31 +219,27 @@ withRepoAndState :: [DarcsFlag] -> (forall rt p wR wU .- (ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree, RepoPatch p) =>+ (ApplyState p ~ Tree, RepoPatch p) => (Repository rt p wR wU wR, Tree IO, Tree IO, Tree IO) -> IO ()) -> IO () withRepoAndState opts f =- withRepoLock dr uc YesUpdateWorking um $ RepoJob $ \repo -> do+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repo -> do work <- readPlainTree "." cur <- readRecordedAndPending repo recorded <- readRecorded repo f (repo, work, cur, recorded)- where- dr = dryRun opts- uc = useCache opts- um = umask opts -simpleMove :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+simpleMove :: (RepoPatch p, ApplyState 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 doMoves repository opts cur work [(old_fp, new_fp)]- unless (Quiet `elem` opts) $- putStrLn $ unwords ["Moved:", old_fp, "to:", new_fp]+ putInfo opts $ hsep $ map text ["Moved:", old_fp, "to:", new_fp] -moveToDir :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+moveToDir :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> [DarcsFlag] -> Tree IO -> Tree IO -> [FilePath] -> FilePath -> IO ()@@ -267,10 +247,9 @@ let movetargets = map ((finaldir </>) . takeFileName) moved moves = zip moved movetargets doMoves repository opts cur work moves- unless (Quiet `elem` opts) $- putStrLn $ unwords $ ["Moved:"] ++ moved ++ ["to:", finaldir]+ putInfo opts $ hsep $ map text $ ["Moved:"] ++ moved ++ ["to:", finaldir] -doMoves :: (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+doMoves :: (RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> [DarcsFlag] -> Tree IO -> Tree IO -> [(FilePath, FilePath)] -> IO ()@@ -317,8 +296,7 @@ then Just <$> deleteNewFromRepoPatches else return Nothing else do- unless (Quiet `elem` opts) $- putStrLn "Detected post-hoc move."+ putInfo opts $ text "Detected post-hoc move." -- Post-hoc move - user has moved/deleted the file in working, so -- we can hopefully make a move patch to make the repository -- consistent.@@ -331,7 +309,7 @@ else return $ emptyGap NilFL where newIsOkWindowsPath =- doAllowWindowsReserved opts || WindowsFilePath.isValid new+ allowWindowsReservedFilenames ? opts || WindowsFilePath.isValid new newNotOkWindowsPathMsg = "The filename " ++ new ++ " is not valid under Windows.\n"@@ -340,9 +318,8 @@ -- If we're moving to a file/dir that was recorded, but has been deleted, -- we need to add patches to pending that remove the original. deleteNewFromRepoPatches = do- unless (Quiet `elem` opts) $- putStrLn $ "Existing recorded contents of " ++ new- ++ " will be overwritten."+ putInfo opts $ text $+ "Existing recorded contents of " ++ new ++ " will be overwritten." ftf <- filetypeFunction let curNoNew = modifyTree cur (floatPath new) Nothing -- Return patches to remove new, so that the move patch@@ -352,10 +329,10 @@ -- Check if the passed tree has the new filepath. The old path is removed -- from the tree before checking if the new path is present. hasNew s = treeHas_case (modifyTree s (floatPath old) Nothing) new- treeHas_case = if doAllowCaseOnly opts then treeHas else treeHasAnycase+ treeHas_case = if allowCaseDifferingFilenames ? opts then treeHas else treeHasAnycase alreadyExists inWhat =- if doAllowCaseOnly opts+ if allowCaseDifferingFilenames ? opts then "A file or dir named "++new++" already exists in " ++ inWhat ++ "." else "A file or dir named "++new++" (or perhaps differing "
@@ -15,56 +15,49 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings #-} -module Darcs.UI.Commands.Optimize ( optimize, doOptimizeHTTP ) where+module Darcs.UI.Commands.Optimize ( optimize ) where import Prelude () import Darcs.Prelude -import Control.Exception ( finally )-import Control.Monad ( when, unless, forM, forM_ )+import Control.Monad ( when, unless, forM_ )+import Data.List ( nub ) import Data.Maybe ( isJust, fromJust )-import Data.List ( sort )-import Data.Set ( difference ) import System.Directory ( getDirectoryContents , doesDirectoryExist- , doesFileExist , renameFile- , getModificationTime , createDirectoryIfMissing , removeFile , getHomeDirectory )-import System.IO.Unsafe ( unsafeInterleaveIO )-import qualified Data.ByteString.Char8 as BS-import qualified Data.ByteString.Lazy as BL---import Darcs.Patch.PatchInfoAnd ( extractHash )+import qualified Data.ByteString.Char8 as BC import Darcs.UI.Commands ( DarcsCommand(..), nodefaults , amInHashedRepository, amInRepository, putInfo , normalCommand, withStdOpts )-import Darcs.Repository.Prefs ( getPreflist, getCaches, globalCacheDir, oldGlobalCacheDir )+import Darcs.UI.Completion ( noArgs )+import Darcs.Repository.Prefs ( getPreflist, getCaches, globalCacheDir ) import Darcs.Repository ( Repository+ , repoLocation , withRepoLock , RepoJob(..) , readRepo , 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.Job ( withOldRepoLock )+import Darcs.Repository.Identify ( findAllReposInDir )+import Darcs.Repository.Hashed ( inventoriesDir, patchesDir, pristineDir,+ hashedInventory,+ listInventoriesRepoDir,+ listPatchesLocalBucketed, diffHashLists, peekPristineHash )+import Darcs.Repository.Packs ( createPacks )+import Darcs.Repository.Pending ( pendingName ) import Darcs.Repository.HashedIO ( getHashedFiles )-import Darcs.Repository.Old ( oldRepoFailMsg )-import Darcs.Repository.InternalTypes ( Repository(..), Pristine(..) ) import Darcs.Patch.Witnesses.Ordered ( mapFL , bunchFL@@ -72,8 +65,8 @@ ) import Darcs.Patch ( IsRepoType, RepoPatch ) import Darcs.Patch.Set- ( newset2RL- , newset2FL+ ( patchSet2RL+ , patchSet2FL , progressPatchSet ) import Darcs.Patch.Apply( ApplyState )@@ -85,8 +78,13 @@ , writeAtomicFilePS , rmRecursive , removeFileMayNotExist+ , writeBinFile )-import Darcs.Util.File ( withCurrentDirectory, getRecursiveContents )+import Darcs.Util.File+ ( withCurrentDirectory+ , getRecursiveContents+ , doesDirectoryReallyExist+ ) import Darcs.UI.External ( catchall ) import Darcs.Util.Progress ( beginTedious@@ -99,17 +97,13 @@ import System.FilePath.Posix ( takeExtension , (</>)- , (<.>)- , takeFileName , joinPath ) import Text.Printf ( printf )-import System.Posix.Files ( getFileStatus, isDirectory ) import Darcs.UI.Flags- ( DarcsFlag(Compress)- , compression, verbosity, useCache, umask )+ ( DarcsFlag, verbosity, useCache, umask ) import Darcs.UI.Options ( DarcsOption, (^), oid, odesc, ocheck, onormalise- , defaultFlags, parseFlags )+ , defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.Flags ( UpdateWorking (..), DryRun ( NoDryRun ), UseCache (..), UMask (..)@@ -125,7 +119,7 @@ , RepoProperty ( HashedInventory ) ) import Darcs.Repository.PatchIndex-import qualified Darcs.Repository.HashedRepo as HashedRepo+import qualified Darcs.Repository.Hashed as HashedRepo import Darcs.Repository.State ( readRecorded ) import Darcs.Util.Tree@@ -141,11 +135,7 @@ ( writeDarcsHashed , decodeDarcsSize )-import Codec.Archive.Tar ( write )-import Codec.Archive.Tar.Entry ( fileEntry, toTarPath )-import Codec.Compression.GZip ( compress ) - optimizeDescription :: String optimizeDescription = "Optimize the repository." @@ -176,24 +166,10 @@ } commonBasicOpts :: DarcsOption a (Maybe String -> UMask -> a)-commonBasicOpts = O.workingRepoDir ^ O.umask+commonBasicOpts = O.repoDir ^ O.umask+ commonAdvancedOpts :: DarcsOption a a commonAdvancedOpts = oid-commonOpts :: DarcsOption a- (Maybe String- -> UMask- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-commonOpts = commonBasicOpts `withStdOpts` commonAdvancedOpts common :: DarcsCommand [DarcsFlag] common = DarcsCommand@@ -206,13 +182,15 @@ , commandHelp = undefined , commandDescription = undefined , commandCommand = undefined- , commandGetArgPossibilities = undefined+ , commandCompleteArgs = noArgs , commandAdvancedOptions = odesc commonAdvancedOpts , commandBasicOptions = odesc commonBasicOpts , commandDefaults = defaultFlags commonOpts , commandCheckOptions = ocheck commonOpts , commandParseOptions = onormalise commonOpts }+ where+ commonOpts = commonBasicOpts `withStdOpts` commonAdvancedOpts optimizeClean :: DarcsCommand [DarcsFlag]@@ -225,7 +203,8 @@ optimizeCleanCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizeCleanCmd _ opts _ =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+ 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!" @@ -248,9 +227,10 @@ optimizeHttpCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizeHttpCmd _ opts _ =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+ withRepoLock NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories- doOptimizeHTTP repository+ createPacks repository putInfo opts "Done creating packs!" optimizePristine :: DarcsCommand [DarcsFlag]@@ -264,7 +244,8 @@ optimizePristineCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizePristineCmd _ opts _ =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+ withRepoLock NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories doOptimizePristine opts repository putInfo opts "Done optimizing pristine!"@@ -287,34 +268,39 @@ optimizeCompressCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizeCompressCmd _ opts _ =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+ withRepoLock NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories- optimizeCompression [Compress]+ optimizeCompression O.GzipCompression opts putInfo opts "Done optimizing by compression!" optimizeUncompressCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizeUncompressCmd _ opts _ =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+ withRepoLock NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories- optimizeCompression []+ optimizeCompression O.NoCompression opts putInfo opts "Done optimizing by uncompression!" -optimizeCompression :: [DarcsFlag] -> IO ()-optimizeCompression opts = do+optimizeCompression :: O.Compression -> [DarcsFlag] -> IO ()+optimizeCompression compression opts = do putInfo opts "Optimizing (un)compression of patches..."- do_compress (darcsdir++"/patches")+ do_compress (darcsdir ++ "/patches") putInfo opts "Optimizing (un)compression of inventories..."- do_compress (darcsdir++"/inventories")- where do_compress f =- do isd <- doesDirectoryExist f- if isd then withCurrentDirectory f $- do fs <- filter notdot `fmap` getDirectoryContents "."- mapM_ do_compress fs- else if Compress `elem` opts- then gzReadFilePS f >>= gzWriteAtomicFilePS f- else gzReadFilePS f >>= writeAtomicFilePS f- notdot ('.':_) = False- notdot _ = True+ do_compress (darcsdir ++ "/inventories")+ where+ do_compress f = do+ isd <- doesDirectoryExist f+ if isd+ then withCurrentDirectory f $ do+ fs <- filter notdot `fmap` getDirectoryContents "."+ mapM_ do_compress fs+ else gzReadFilePS f >>=+ case compression of+ O.GzipCompression -> gzWriteAtomicFilePS f+ O.NoCompression -> writeAtomicFilePS f+ notdot ('.':_) = False+ notdot _ = True optimizeEnablePatchIndex :: DarcsCommand [DarcsFlag] optimizeEnablePatchIndex = common@@ -336,14 +322,17 @@ optimizeEnablePatchIndexCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizeEnablePatchIndexCmd _ opts _ =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do- createOrUpdatePatchIndexDisk repository+ withRepoLock NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repository -> do+ ps <- readRepo repository+ createOrUpdatePatchIndexDisk repository ps putInfo opts "Done enabling patch index!" optimizeDisablePatchIndexCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizeDisablePatchIndexCmd _ opts _ =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \(Repo repodir _ _ _) -> do- deletePatchIndex repodir+ withRepoLock NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repo -> do+ deletePatchIndex (repoLocation repo) putInfo opts "Done disabling patch index!" optimizeReorder :: DarcsCommand [DarcsFlag]@@ -359,30 +348,11 @@ optimizeReorderCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizeReorderCmd _ opts _ =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do- reorderInventory repository (compression opts) YesUpdateWorking (verbosity opts)+ withRepoLock NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repository -> do+ reorderInventory repository (O.compress ? opts) YesUpdateWorking (verbosity ? opts) putInfo opts "Done reordering!" -optimizeRelinkBasicOpts :: DarcsOption a- (Maybe String -> UMask -> [AbsolutePath] -> a)-optimizeRelinkBasicOpts = commonBasicOpts ^ O.siblings-optimizeRelinkOpts :: DarcsOption a- (Maybe String- -> UMask- -> [AbsolutePath]- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-optimizeRelinkOpts = optimizeRelinkBasicOpts `withStdOpts` commonAdvancedOpts- optimizeRelink :: DarcsCommand [DarcsFlag] optimizeRelink = common { commandName = "relink"@@ -395,10 +365,14 @@ , commandCheckOptions = ocheck optimizeRelinkOpts , commandParseOptions = onormalise optimizeRelinkOpts }+ where+ optimizeRelinkBasicOpts = commonBasicOpts ^ O.siblings+ optimizeRelinkOpts = optimizeRelinkBasicOpts `withStdOpts` commonAdvancedOpts optimizeRelinkCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizeRelinkCmd _ opts _ =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+ withRepoLock NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repository -> do cleanRepository repository -- garbage collect pristine.hashed, inventories and patches directories doRelink opts putInfo opts "Done relinking!"@@ -434,14 +408,14 @@ "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 rt p wR wU wT -> IO ()+doOptimizePristine :: [DarcsFlag] -> Repository rt p wR wU wT -> IO () doOptimizePristine opts repo = do- inv <- BS.readFile (darcsdir </> "hashed_inventory")- let linesInv = BS.split '\n' inv+ inv <- BC.readFile (darcsdir </> "hashed_inventory")+ let linesInv = BC.split '\n' inv case linesInv of [] -> return () (pris_line:_) ->- let size = decodeDarcsSize $ BS.drop 9 pris_line+ let size = decodeDarcsSize $ BC.drop 9 pris_line in when (isJust size) $ do putInfo opts "Optimizing hashed pristine..." readRecorded repo >>= replacePristine repo cleanRepository repo@@ -473,14 +447,12 @@ -- Only 'optimize' commands that works on old-fashionned repositories optimizeUpgradeCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () optimizeUpgradeCmd _ opts _ = do- debugMessage "Upgrading to hashed..." rf <- identifyRepoFormat "." debugMessage "Found our format" if formatHas HashedInventory rf then putInfo opts "No action taken because this repository already is hashed."- else do putInfo opts "Checking repository in case of corruption..."- withRepoLock NoDryRun YesUseCache YesUpdateWorking NoUMask $ RepoJob $ \repository ->- actuallyUpgradeFormat repository+ else do putInfo opts "Upgrading to hashed..."+ withOldRepoLock $ RepoJob actuallyUpgradeFormat actuallyUpgradeFormat :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)@@ -490,98 +462,48 @@ patches <- readRepo repository let k = "Hashing patch" beginTedious k- tediousSize k (lengthRL $ newset2RL patches)+ tediousSize k (lengthRL $ patchSet2RL patches) let patches' = progressPatchSet k patches cache <- getCaches YesUseCache "."- let compr = compression [] -- default compression- HashedRepo.writeTentativeInventory cache compr patches'+ -- TODO Why use the default and not what the user provided?+ -- Is it because the author couldn't be bothered to add the option?+ -- Or is there a more profound reason?+ -- Such things justify a few lines of comment!+ let compressDefault = O.compress ? []+ HashedRepo.writeTentativeInventory cache compressDefault patches' endTedious k -- convert pristine by applying patches -- the faster alternative would be to copy pristine, but the apply method is more reliable- let patchesToApply = progressFL "Applying patch" $ newset2FL patches'+ let patchesToApply = progressFL "Applying patch" $ patchSet2FL patches' createDirectoryIfMissing False $ darcsdir </> hashedDir HashedPristineDir -- We ignore the returned root hash, we don't use it.- _ <- writeDarcsHashed emptyTree $ darcsdir </> "pristine.hashed"- sequence_ $ mapFL HashedRepo.applyToTentativePristine $ bunchFL 100 patchesToApply+ _ <- writeDarcsHashed emptyTree $ darcsdir </> hashedDir HashedPristineDir+ writeBinFile (darcsdir++"/tentative_pristine") ""+ sequence_ $ mapFL HashedRepo.applyToTentativePristineCwd $ bunchFL 100 patchesToApply -- now make it official- HashedRepo.finalizeTentativeChanges repository compr+ HashedRepo.finalizeTentativeChanges repository compressDefault writeRepoFormat (createRepoFormat PatchFormat1 WithWorkingDir) (darcsdir </> "format") -- clean out old-fashioned junk debugMessage "Cleaning out old-fashioned repository files..."- removeFile $ darcsdir </> "inventory"- removeFile $ darcsdir </> "tentative_inventory"+ removeFileMayNotExist $ darcsdir </> "inventory"+ removeFileMayNotExist $ darcsdir </> "tentative_inventory" rmRecursive (darcsdir </> "pristine") `catchall` rmRecursive (darcsdir </> "current") rmGzsIn (darcsdir </> "patches") rmGzsIn (darcsdir </> "inventories") let checkpointDir = darcsdir </> "checkpoints" hasCheckPoints <- doesDirectoryExist checkpointDir when hasCheckPoints $ rmRecursive checkpointDir+ removeFileMayNotExist (pendingName ++ ".tentative")+ removeFileMayNotExist pendingName where rmGzsIn dir = withCurrentDirectory dir $ do gzs <- filter ((== ".gz") . takeExtension) `fmap` getDirectoryContents "." mapM_ removeFile gzs -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"- , basicTar <.> "part"- , 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- Just hash <- readHashedPristineRoot repo- writeFile ( packsDir </> "pristine" ) hash- -- pack patchesTar- ps <- mapFL hashedPatchFileName . newset2FL <$> readRepo repo- 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)- renameFile (patchesTar <.> "part") patchesTar- -- pack basicTar- pr <- sortByMTime =<< dirContents "pristine.hashed"- writeFile (darcsdir </> "meta-filelist-pristine") . unlines $- map takeFileName pr- BL.writeFile (basicTar <.> "part") . compress . write =<< mapM fileEntry' (- [ darcsdir </> "meta-filelist-pristine"- , darcsdir </> "hashed_inventory"- ] ++ reverse pr)- renameFile (basicTar <.> "part") basicTar- where- packsDir = darcsdir </> "packs"- basicTar = packsDir </> "basic.tar.gz"- patchesTar = packsDir </> "patches.tar.gz"- fileEntry' x = unsafeInterleaveIO $ do- content <- BL.fromChunks . return <$> gzReadFilePS x- tp <- either fail return $ toTarPath False x- return $ fileEntry tp content- dirContents d = map ((darcsdir </> d) </>) <$>- filterDirContents d (const True)- hashedPatchFileName x = case extractHash x of- Left _ -> fail "unexpected unhashed patch"- Right h -> darcsdir </> "patches" </> h- sortByMTime xs = map snd . sort <$> mapM (\x -> (\t -> (t, x)) <$>- getModificationTime x) xs- removeFileIfExists x = do- ex <- doesFileExist x- when ex $ removeFile x- optimizeBucketed :: [DarcsFlag] -> IO () optimizeBucketed opts = do putInfo opts "Migrating global cache to bucketed format."- gOldCacheDir <- oldGlobalCacheDir gCacheDir <- globalCacheDir case gCacheDir of@@ -594,13 +516,6 @@ toBucketed gCachePristineDir gCachePristineDir toBucketed gCacheInventoriesDir gCacheInventoriesDir toBucketed gCachePatchesDir gCachePatchesDir- case gOldCacheDir of- Nothing -> debugMessage "Old global cache doesn't exist."- Just gOldCacheDir' -> do- debugMessage "Making bucketed cache from old cache."- toBucketed (joinPath [gOldCacheDir', pristineDir]) gCachePristineDir- toBucketed (joinPath [gOldCacheDir', inventoriesDir]) gCacheInventoriesDir- toBucketed (joinPath [gOldCacheDir', patchesDir]) gCachePatchesDir putInfo opts "Done making bucketed cache!" where toBucketed :: FilePath -> FilePath -> IO ()@@ -613,8 +528,8 @@ createDirectoryIfMissing True (dest </> subDir) fileNames <- getDirectoryContents src forM_ fileNames $ \file -> do- fileStatus <- getFileStatus (src </> file)- if not $ isDirectory fileStatus+ exists <- doesDirectoryReallyExist (src </> file)+ if not $ exists then renameFile' src dest file else return () else do@@ -668,11 +583,11 @@ putInfo opts "\nLooking for repositories in the following directories:" putInfo opts $ text $ unlines dirs gCacheDir' <- globalCacheDir- repoPaths' <- mapM getRecursiveDarcsRepos dirs+ repoPaths' <- mapM findAllReposInDir dirs putInfo opts "Finished listing repositories." - let repoPaths = unset . set $ concat repoPaths'+ let repoPaths = nub $ concat repoPaths' gCache = fromJust gCacheDir' gCacheInvDir = gCache </> inventoriesDir gCachePatchesDir = gCache </> patchesDir@@ -696,33 +611,11 @@ remove' dir s1 s2 = mapM_ (removeFileMayNotExist . (\hashedFile -> dir </> bucketFolder hashedFile </> hashedFile))- (unset $ set s1 `difference` set s2)+ (diffHashLists s1 s2) getPristine :: String -> IO [String] getPristine darcsDir = do i <- gzReadFilePS (darcsDir </> darcsdir </> hashedInventory)- getHashedFiles (darcsDir </> darcsdir </> pristineDir) [inv2pris i]+ getHashedFiles (darcsDir </> darcsdir </> pristineDir) [peekPristineHash 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)
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.UI.Commands.Pull ( -- * Commands. pull, fetch, pullCmd, StandardPatchApplier,@@ -27,44 +25,29 @@ import Prelude () import Darcs.Prelude -import Prelude hiding ( (^) )- import System.Exit ( exitSuccess ) import Control.Monad ( when, unless, (>=>) ) import Data.List ( nub ) import Data.Maybe ( fromMaybe ) import Darcs.UI.Commands- ( DarcsCommand(..), withStdOpts+ ( DarcsCommand(..)+ , withStdOpts , putInfo+ , putVerbose , setEnvDarcsPatches , defaultRepo , amInHashedRepository ) import Darcs.UI.Flags ( DarcsFlag- ( AllowConflicts- , Complement- , DryRun- , Intersection- , MarkConflicts- , NoAllowConflicts- , SkipConflicts- , Verbose- , XMLOutput- , Quiet- , AllowUnrelatedRepos- ) , fixUrl, getOutput- , doReverse, verbosity, dryRun, umask, useCache, selectDeps+ , changesReverse, verbosity, dryRun, umask, useCache, selectDeps , remoteRepos, reorder, setDefault- , isUnified, hasSummary- , isInteractive- )-import Darcs.UI.Options- ( DarcsOption, (^), odesc, ocheck, onormalise- , defaultFlags, parseFlags+ , withContext, hasXmlOutput+ , isInteractive, quiet )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.Flags ( UpdateWorking (..) ) import Darcs.Repository@@ -73,7 +56,6 @@ , withRepoLock , RepoJob(..) , readRepo- , checkUnrelatedRepos , modifyCache , modifyCache , Cache(..)@@ -84,7 +66,7 @@ import qualified Darcs.Repository.Cache as DarcsCache import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info, hopefully, patchDesc )-import Darcs.Patch ( IsRepoType, RepoPatch, description, PrimOf )+import Darcs.Patch ( IsRepoType, RepoPatch, description ) import Darcs.Patch.Bundle( makeBundleN, patchFilename ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Set ( Origin, PatchSet(..), SealedPatchSet )@@ -94,11 +76,12 @@ ( (:>)(..), (:\/:)(..), FL(..), RL(..) , mapFL, nullFL, reverseFL, mapFL_FL ) import Darcs.Patch.Permutations ( partitionFL )-import Darcs.Repository.Prefs ( addToPreflist, addRepoSource, getPreflist )-import Darcs.Repository.Motd (showMotd )+import Darcs.Repository.Prefs ( addToPreflist, addRepoSource, getPreflist, showMotd ) import Darcs.Patch.Depends ( findUncommon, findCommonWithThem,- newsetIntersection, newsetUnion )+ patchSetIntersection, patchSetUnion ) import Darcs.UI.ApplyPatches ( PatchApplier(..), StandardPatchApplier(..) )+import Darcs.UI.Completion ( prefArgs )+import Darcs.UI.Commands.Util ( checkUnrelatedRepos ) import Darcs.UI.SelectChanges ( WhichChanges(..) , runSelection@@ -106,15 +89,13 @@ ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions (..) ) import Darcs.Util.Exception ( clarifyErrors )-import Darcs.Util.Printer ( putDocLn, vcat, ($$), text, putDoc )+import Darcs.Util.Printer ( vcat, ($$), text, putDoc ) import Darcs.Util.Lock ( writeDocBinFile ) import Darcs.Util.Path ( useAbsoluteOrStd, stdOut, AbsolutePath ) import Darcs.Util.Workaround ( getCurrentDirectory ) import Darcs.Util.Text ( quote ) import Darcs.Util.Tree( Tree )-#include "impossible.h" - pullDescription :: String pullDescription = "Copy and apply patches from another repository to this one."@@ -154,157 +135,6 @@ , "the help of `pull` to know more." ] -pullBasicOpts :: DarcsOption a- ([O.MatchFlag]- -> O.Reorder- -> Maybe Bool- -> Maybe O.AllowConflicts- -> O.ExternalMerge- -> O.RunTest- -> O.DryRun- -> O.XmlOutput- -> Maybe O.Summary- -> O.SelectDeps- -> Maybe Bool- -> Maybe String- -> Bool- -> O.DiffAlgorithm- -> a)-pullBasicOpts- = O.matchSeveral- ^ O.reorder- ^ O.interactive -- True- ^ O.conflicts O.YesAllowConflictsAndMark- ^ O.useExternalMerge- ^ O.test- ^ O.dryRunXml- ^ O.summary- ^ O.selectDeps- ^ O.setDefault- ^ O.workingRepoDir- ^ O.allowUnrelatedRepos- ^ O.diffAlgorithm--pullAdvancedOpts :: DarcsOption a- (O.RepoCombinator- -> O.Compression- -> O.UseIndex- -> O.RemoteRepos- -> O.SetScriptsExecutable- -> O.UMask- -> Bool- -> Bool- -> O.WantGuiPause- -> O.NetworkOptions- -> a)-pullAdvancedOpts- = O.repoCombinator- ^ O.compress- ^ O.useIndex- ^ O.remoteRepos- ^ O.setScriptsExecutable- ^ O.umask- ^ O.restrictPaths- ^ O.changesReverse- ^ O.pauseForGui- ^ O.network--pullOpts :: DarcsOption a- ([O.MatchFlag]- -> O.Reorder- -> Maybe Bool- -> Maybe O.AllowConflicts- -> O.ExternalMerge- -> O.RunTest- -> O.DryRun- -> O.XmlOutput- -> Maybe O.Summary- -> O.SelectDeps- -> Maybe Bool- -> Maybe String- -> Bool- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.RepoCombinator- -> O.Compression- -> O.UseIndex- -> O.RemoteRepos- -> O.SetScriptsExecutable- -> O.UMask- -> Bool- -> Bool- -> O.WantGuiPause- -> O.NetworkOptions- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-pullOpts = pullBasicOpts `withStdOpts` pullAdvancedOpts--fetchBasicOpts :: DarcsOption a- ([O.MatchFlag]- -> Maybe Bool- -> O.DryRun- -> Maybe O.Summary- -> O.SelectDeps- -> Maybe Bool- -> Maybe String- -> Maybe O.Output- -> Bool- -> O.DiffAlgorithm- -> a)-fetchBasicOpts- = O.matchSeveral- ^ O.interactive -- True- ^ O.dryRun- ^ O.summary- ^ O.selectDeps- ^ O.setDefault- ^ O.workingRepoDir- ^ O.output- ^ O.allowUnrelatedRepos- ^ O.diffAlgorithm--fetchAdvancedOpts :: DarcsOption a- (O.RepoCombinator -> O.RemoteRepos -> O.NetworkOptions -> a)-fetchAdvancedOpts- = O.repoCombinator- ^ O.remoteRepos- ^ O.network--fetchOpts :: DarcsOption a- ([O.MatchFlag]- -> Maybe Bool- -> O.DryRun- -> Maybe O.Summary- -> O.SelectDeps- -> Maybe Bool- -> Maybe String- -> Maybe O.Output- -> Bool- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.RepoCombinator- -> O.RemoteRepos- -> O.NetworkOptions- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-fetchOpts = fetchBasicOpts `withStdOpts` fetchAdvancedOpts- fetch :: DarcsCommand [DarcsFlag] fetch = DarcsCommand { commandProgramName = "darcs"@@ -315,14 +145,31 @@ , commandExtraArgHelp = ["[REPOSITORY]..."] , commandCommand = fetchCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = getPreflist "repos"+ , commandCompleteArgs = prefArgs "repos" , commandArgdefaults = defaultRepo- , commandAdvancedOptions = odesc fetchAdvancedOpts- , commandBasicOptions = odesc fetchBasicOpts- , commandDefaults = defaultFlags fetchOpts- , commandCheckOptions = ocheck fetchOpts- , commandParseOptions = onormalise fetchOpts+ , commandAdvancedOptions = odesc advancedOpts+ , commandBasicOptions = odesc basicOpts+ , commandDefaults = defaultFlags allOpts+ , commandCheckOptions = ocheck allOpts+ , commandParseOptions = onormalise allOpts }+ where+ basicOpts+ = O.matchSeveral+ ^ O.interactive -- True+ ^ O.dryRun+ ^ O.summary+ ^ O.selectDeps+ ^ O.setDefault+ ^ O.repoDir+ ^ O.output+ ^ O.allowUnrelatedRepos+ ^ O.diffAlgorithm+ advancedOpts+ = O.repoCombinator+ ^ O.remoteRepos+ ^ O.network+ allOpts = basicOpts `withStdOpts` advancedOpts pull :: DarcsCommand [DarcsFlag] pull = DarcsCommand@@ -334,44 +181,67 @@ , commandExtraArgHelp = ["[REPOSITORY]..."] , commandCommand = pullCmd StandardPatchApplier , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = getPreflist "repos"+ , commandCompleteArgs = prefArgs "repos" , commandArgdefaults = defaultRepo- , commandAdvancedOptions = odesc pullAdvancedOpts- , commandBasicOptions = odesc pullBasicOpts- , commandDefaults = defaultFlags pullOpts- , commandCheckOptions = ocheck pullOpts- , commandParseOptions = onormalise pullOpts+ , commandAdvancedOptions = odesc advancedOpts+ , commandBasicOptions = odesc basicOpts+ , commandDefaults = defaultFlags allOpts+ , commandCheckOptions = ocheck allOpts+ , commandParseOptions = onormalise allOpts }--mergeOpts :: [DarcsFlag] -> [DarcsFlag]-mergeOpts opts | NoAllowConflicts `elem` opts = opts- | AllowConflicts `elem` opts = opts- | otherwise = MarkConflicts : opts+ where+ basicOpts+ = O.matchSeveral+ ^ O.reorder+ ^ O.interactive+ ^ O.conflictsYes+ ^ O.externalMerge+ ^ O.runTest+ ^ O.dryRunXml+ ^ O.summary+ ^ O.selectDeps+ ^ O.setDefault+ ^ O.repoDir+ ^ O.allowUnrelatedRepos+ ^ O.diffAlgorithm+ advancedOpts+ = O.repoCombinator+ ^ O.compress+ ^ O.useIndex+ ^ O.remoteRepos+ ^ O.setScriptsExecutable+ ^ O.umask+ ^ O.restrictPaths+ ^ O.changesReverse+ ^ O.pauseForGui+ ^ O.network+ allOpts = basicOpts `withStdOpts` advancedOpts -pullCmd :: PatchApplier pa => pa -> (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()+pullCmd+ :: PatchApplier pa+ => pa -> (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () pullCmd patchApplier (_,o) opts repos = do pullingFrom <- mapM (fixUrl o) repos- withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $ repoJob patchApplier opts $ \patchProxy initRepo -> do let repository = modifyCache initRepo $ addReposToCache pullingFrom (_, Sealed (us' :\/: to_be_pulled))- <- fetchPatches o opts' repos "pull" repository+ <- fetchPatches o opts repos "pull" repository let from_whom = error "Internal error: pull shouldn't need a 'from' address"- applyPatches patchApplier patchProxy "pull" opts' from_whom repository us' to_be_pulled+ applyPatches patchApplier patchProxy "pull" opts from_whom repository us' to_be_pulled where- opts' = mergeOpts opts addReposToCache repos' (Ca cache) = Ca $ [ toReadOnlyCache r | r <- repos' ] ++ cache toReadOnlyCache = Cache DarcsCache.Repo NotWritable fetchCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () fetchCmd (_,o) opts repos =- withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $ RepoJob $ fetchPatches o opts repos "fetch" >=> makeBundle opts -fetchPatches :: forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+fetchPatches :: forall rt p wR wU . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => AbsolutePath -> [DarcsFlag] -> [String] -> String -> Repository rt p wR wU wR -> IO (SealedPatchSet rt p Origin,@@ -383,15 +253,17 @@ when (null repodirs) $ fail "Can't pull from current repository!" old_default <- getPreflist "defaultrepo"- when (old_default == repodirs && XMLOutput `notElem` opts) $- let pulling = if DryRun `elem` opts then "Would pull" else "Pulling"+ when (old_default == repodirs && not (hasXmlOutput opts)) $+ let pulling = case dryRun ? opts of+ O.YesDryRun -> "Would pull"+ O.NoDryRun -> "Pulling" 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)+ addRepoSource (head repodirs) (dryRun ? opts) (remoteRepos ? opts) (setDefault False opts) mapM_ (addToPreflist "repos") repodirs- unless (Quiet `elem` opts || XMLOutput `elem` opts) $ mapM_ showMotd repodirs+ unless (quiet opts || hasXmlOutput opts) $ mapM_ showMotd repodirs us <- readRepo repository- checkUnrelatedRepos (AllowUnrelatedRepos `elem` opts) us them+ checkUnrelatedRepos (parseFlags O.allowUnrelatedRepos opts) us them common :> _ <- return $ findCommonWithThem us them us' :\/: them' <- return $ findUncommon us them@@ -399,22 +271,24 @@ let avoided = mapFL info compl' ps :> _ <- return $ partitionFL (not . (`elem` avoided) . info) them'- when (Verbose `elem` opts) $- do case us' of- (x@(_:>:_)) -> putDocLn $ text "We have the following new (to them) patches:"- $$ vcat (mapFL description x)- _ -> return ()- unless (nullFL ps) $ putDocLn $ text "They have the following patches to pull:"- $$ vcat (mapFL description ps)+ putVerbose opts $+ case us' of+ (x@(_ :>: _)) ->+ text "We have the following new (to them) patches:" $$+ vcat (mapFL description x)+ _ -> mempty+ unless (nullFL ps) $ putVerbose opts $+ text "They have the following patches to pull:" $$+ vcat (mapFL description ps) (hadConflicts, Sealed psFiltered)- <- if SkipConflicts `elem` opts+ <- if O.conflictsYes ? opts == Nothing then filterOutConflicts (reverseFL us') repository ps else return (False, Sealed ps)- when hadConflicts $ putStrLn "Skipping some patches which would cause conflicts."- when (nullFL psFiltered) $ do putInfo opts $ text "No remote patches to pull in!"- setEnvDarcsPatches psFiltered- when (reorder opts /= O.Reorder) exitSuccess- let direction = if doReverse opts then FirstReversed else First+ when hadConflicts $ putInfo opts $ text "Skipping some patches which would cause conflicts."+ when (nullFL psFiltered) $ do putInfo opts $ text "No remote patches to pull in!"+ setEnvDarcsPatches psFiltered+ when (reorder ? opts /= O.Reorder) exitSuccess+ let direction = if changesReverse ? opts then FirstReversed else First context = selectionContext direction jobname (pullPatchSelOpts opts) Nothing Nothing (to_be_pulled :> _) <- runSelection psFiltered context return (seal common, seal $ us' :\/: to_be_pulled)@@ -464,26 +338,25 @@ the second patchset(s) to be complemented against Rc. -} -readRepos :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+readRepos :: (IsRepoType rt, RepoPatch p) => 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+ do rs <- mapM (\u -> do r <- identifyRepositoryFor to_repo (useCache ? opts) u ps <- readRepo r return $ seal ps) us- return $ if Intersection `elem` opts- then (newsetIntersection rs, seal (PatchSet NilRL NilRL))- else if Complement `elem` opts- then (head rs, newsetUnion $ tail rs)- else (newsetUnion rs, seal (PatchSet NilRL NilRL))+ return $ case parseFlags O.repoCombinator opts of+ O.Intersection -> (patchSetIntersection rs, seal (PatchSet NilRL NilRL))+ O.Complement -> (head rs, patchSetUnion $ tail rs)+ O.Union -> (patchSetUnion rs, seal (PatchSet NilRL NilRL)) pullPatchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions pullPatchSelOpts flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = parseFlags O.matchSeveral flags , S.interactive = isInteractive True flags- , S.selectDeps = selectDeps flags- , S.summary = hasSummary O.NoSummary flags- , S.withContext = isUnified flags+ , S.selectDeps = selectDeps ? flags+ , S.summary = O.summary ? flags+ , S.withContext = withContext ? flags }
@@ -15,15 +15,13 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, TypeOperators #-}+{-# LANGUAGE TypeOperators #-} 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.Maybe ( isJust )@@ -32,25 +30,26 @@ , putVerbose , putInfo , abortRun- , printDryRunMessageAndExit , setEnvDarcsPatches , defaultRepo , amInHashedRepository )+import Darcs.UI.Commands.Util ( printDryRunMessageAndExit, checkUnrelatedRepos )+import Darcs.UI.Completion ( prefArgs ) import Darcs.UI.Flags ( DarcsFlag- , isInteractive, verbosity, isUnified, hasSummary- , hasXmlOutput, selectDeps, applyAs, remoteDarcs- , doReverse, dryRun, useCache, remoteRepos, setDefault, fixUrl )+ , isInteractive, verbosity, withContext+ , xmlOutput, selectDeps, applyAs, remoteDarcs+ , changesReverse, dryRun, useCache, remoteRepos, setDefault, fixUrl ) import Darcs.UI.Options- ( DarcsOption, (^), odesc, ocheck, onormalise- , defaultFlags, parseFlags )+ ( (^), 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 )+ readRepo ) import Darcs.Patch ( IsRepoType, RepoPatch, description ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Witnesses.Ordered@@ -59,6 +58,7 @@ import Darcs.Repository.Prefs ( addRepoSource, getPreflist ) import Darcs.UI.External ( signString, darcsProgram , pipeDoc, pipeDocSSH )+import Darcs.Util.Exception ( die ) import Darcs.Util.URL ( isHttpUrl, isValidLocalPath , isSshUrl, splitSshUrl, SshFilePath(..) ) import Darcs.Util.Path ( AbsolutePath )@@ -70,15 +70,14 @@ import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions (..) ) import Darcs.Patch.Depends ( findCommonWithThem, countUsThem ) import Darcs.Patch.Bundle ( makeBundleN )-import Darcs.Patch.Patchy( ShowPatch )+import Darcs.Patch.Show( ShowPatch ) import Darcs.Patch.Set ( PatchSet, Origin )-import Darcs.Util.Printer ( Doc, vcat, empty, text, ($$), RenderMode(..) )+import Darcs.Util.Printer ( Doc, vcat, empty, text, ($$) ) import Darcs.UI.Email ( makeEmail ) import Darcs.Util.English (englishNum, Noun(..)) import Darcs.Util.Workaround ( getCurrentDirectory ) import Darcs.Util.Text ( quote ) import Darcs.Util.Tree( Tree )-#include "impossible.h" pushDescription :: String@@ -103,78 +102,17 @@ , "you should try again with the `--no-compress` option." ] -pushBasicOpts :: DarcsOption a- ([O.MatchFlag]- -> O.SelectDeps- -> Maybe Bool- -> O.Sign- -> O.DryRun- -> O.XmlOutput- -> Maybe O.Summary- -> Maybe String- -> Maybe Bool- -> Bool- -> a)-pushBasicOpts- = O.matchSeveral- ^ O.selectDeps- ^ O.interactive- ^ O.sign- ^ O.dryRunXml- ^ O.summary- ^ O.workingRepoDir- ^ O.setDefault- ^ O.allowUnrelatedRepos--pushAdvancedOpts :: DarcsOption a- (Maybe String -> O.RemoteRepos -> Bool -> O.Compression -> O.NetworkOptions -> a)-pushAdvancedOpts- = O.applyAs- ^ O.remoteRepos- ^ O.changesReverse- ^ O.compress- ^ O.network--pushOpts :: DarcsOption a- ([O.MatchFlag]- -> O.SelectDeps- -> Maybe Bool- -> O.Sign- -> DryRun- -> O.XmlOutput- -> Maybe O.Summary- -> Maybe String- -> Maybe Bool- -> Bool- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> Maybe String- -> O.RemoteRepos- -> Bool- -> O.Compression- -> O.NetworkOptions- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-pushOpts = pushBasicOpts `withStdOpts` pushAdvancedOpts- push :: DarcsCommand [DarcsFlag] push = DarcsCommand { commandProgramName = "darcs" , commandName = "push" , commandHelp = pushHelp , commandDescription = pushDescription- , commandExtraArgs = 1+ , commandExtraArgs = -1 , commandExtraArgHelp = ["[REPOSITORY]"] , commandCommand = pushCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = getPreflist "repos"+ , commandCompleteArgs = prefArgs "repos" , commandArgdefaults = defaultRepo , commandAdvancedOptions = odesc pushAdvancedOpts , commandBasicOptions = odesc pushBasicOpts@@ -182,43 +120,61 @@ , commandCheckOptions = ocheck pushOpts , commandParseOptions = onormalise pushOpts }+ where+ pushBasicOpts+ = O.matchSeveral+ ^ O.selectDeps+ ^ O.interactive+ ^ O.sign+ ^ O.dryRunXml+ ^ O.summary+ ^ O.repoDir+ ^ O.setDefault+ ^ O.allowUnrelatedRepos+ pushAdvancedOpts+ = O.applyAs+ ^ O.remoteRepos+ ^ O.changesReverse+ ^ O.compress+ ^ O.network+ pushOpts = pushBasicOpts `withStdOpts` pushAdvancedOpts pushCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-pushCmd _ _ [""] = impossible-pushCmd (_,o) opts [unfixedrepodir] =- do- repodir <- fixUrl o unfixedrepodir- -- Test to make sure we aren't trying to push to the current repo- here <- getCurrentDirectory- checkOptionsSanity opts repodir- when (repodir == here) $- fail "Cannot push from repository to itself."- -- absolute '.' also taken into account by fix_filepath- bundle <- withRepository (useCache opts) $ RepoJob $- prepareBundle opts repodir- sbundle <- signString (parseFlags O.sign opts) bundle- let body = if isValidLocalPath repodir- then sbundle- else makeEmail repodir [] Nothing Nothing sbundle Nothing- rval <- remoteApply opts repodir body- case rval of ExitFailure ec -> do putStrLn "Apply failed!"- exitWith (ExitFailure ec)- ExitSuccess -> putInfo opts $ text "Push successful."-pushCmd _ _ _ = impossible+pushCmd (_, o) opts [unfixedrepodir] = do+ repodir <- fixUrl o unfixedrepodir+ here <- getCurrentDirectory+ checkOptionsSanity opts repodir+ -- make sure we aren't trying to push to the current repo+ when (repodir == here) $ die "Cannot push from repository to itself."+ bundle <-+ withRepository (useCache ? opts) $ RepoJob $ prepareBundle opts repodir+ sbundle <- signString (parseFlags O.sign opts) bundle+ let body =+ if isValidLocalPath repodir+ then sbundle+ else makeEmail repodir [] Nothing Nothing sbundle Nothing+ rval <- remoteApply opts repodir body+ case rval of+ ExitFailure ec -> do+ putStrLn "Apply failed!"+ exitWith (ExitFailure ec)+ ExitSuccess -> putInfo opts $ text "Push successful."+pushCmd _ _ [] = die "No default repository to push to, please specify one."+pushCmd _ _ _ = die "Cannot push to more than one repo." 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"+ let pushing = if dryRun ? opts == YesDryRun then "Would push" else "Pushing" in putInfo opts $ text $ pushing++" to "++quote repodir++"..."- them <- identifyRepositoryFor repository (useCache opts) repodir >>= readRepo- addRepoSource repodir (dryRun opts) (remoteRepos opts) (setDefault False opts)+ them <- identifyRepositoryFor repository (useCache ? opts) repodir >>= readRepo+ addRepoSource repodir (dryRun ? opts) (remoteRepos ? opts) (setDefault False opts) us <- readRepo repository common :> us' <- return $ findCommonWithThem us them prePushChatter opts us (reverseFL us') them- let direction = if doReverse opts then FirstReversed else First+ let direction = if changesReverse ? opts then FirstReversed else First context = selectionContext direction "push" (pushPatchSelOpts opts) Nothing Nothing runSelection us' context >>= bundlePatches opts common@@ -246,10 +202,10 @@ do setEnvDarcsPatches to_be_pushed printDryRunMessageAndExit "push"- (verbosity opts)- (hasSummary O.NoSummary opts)- (dryRun opts)- (hasXmlOutput opts)+ (verbosity ? opts)+ (O.summary ? opts)+ (dryRun ? opts)+ (xmlOutput ? opts) (isInteractive True opts) to_be_pushed when (nullFL to_be_pushed) $ do@@ -261,7 +217,7 @@ checkOptionsSanity :: [DarcsFlag] -> String -> IO () checkOptionsSanity opts repodir = if isHttpUrl repodir then do- when (isJust $ applyAs opts) $+ when (isJust $ applyAs ? opts) $ abortRun opts $ text "Cannot --apply-as when pushing to URLs" let lprot = takeWhile (/= ':') repodir msg = text ("Pushing to "++lprot++" URLs is not supported.")@@ -272,17 +228,17 @@ pushPatchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions pushPatchSelOpts flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = parseFlags O.matchSeveral flags , S.interactive = isInteractive True flags- , S.selectDeps = selectDeps flags- , S.summary = hasSummary O.NoSummary flags- , S.withContext = isUnified flags+ , S.selectDeps = selectDeps ? flags+ , S.summary = O.summary ? flags+ , S.withContext = withContext ? flags } remoteApply :: [DarcsFlag] -> String -> Doc -> IO ExitCode remoteApply opts repodir bundle- = case applyAs opts of+ = case applyAs ? opts of Nothing | isSshUrl repodir -> applyViaSsh opts (splitSshUrl repodir) bundle | otherwise -> applyViaLocal opts repodir bundle@@ -293,22 +249,22 @@ applyViaSudo :: String -> String -> Doc -> IO ExitCode applyViaSudo user repo bundle = darcsProgram >>= \darcs ->- pipeDoc Standard "sudo" ["-u",user,darcs,"apply","--all","--repodir",repo] bundle+ pipeDoc "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+ pipeDoc darcs ("apply":"--all":"--repodir":repo:applyopts opts) bundle applyViaSsh :: [DarcsFlag] -> SshFilePath -> Doc -> IO ExitCode applyViaSsh opts repo =- pipeDocSSH (parseFlags O.compress opts) Standard repo+ pipeDocSSH (parseFlags O.compress opts) 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+ pipeDocSSH (parseFlags O.compress opts) repo ["sudo -u "++username++" "++R.remoteDarcs (remoteDarcs opts)++ " apply --all --repodir '"++sshRepo repo++"'"]
@@ -2,7 +2,7 @@ -- -- BSD3 -{-# LANGUAGE CPP, TypeOperators #-}+{-# LANGUAGE TypeOperators #-} module Darcs.UI.Commands.Rebase ( rebase ) where @@ -16,34 +16,29 @@ , defaultRepo, nodefaults , putInfo, putVerbose , setEnvDarcsPatches- , printDryRunMessageAndExit , amInHashedRepository )+import Darcs.UI.Commands.Util ( printDryRunMessageAndExit ) import Darcs.UI.Commands.Apply ( applyCmd ) import Darcs.UI.Commands.Log ( changelog, getLogInfo ) import Darcs.UI.Commands.Pull ( pullCmd, revertable ) import Darcs.UI.Commands.Unrecord ( getLastPatches, matchingHead ) import Darcs.UI.CommandsAux ( checkPaths )+import Darcs.UI.Completion ( fileArgs, prefArgs, noArgs ) import Darcs.UI.Flags ( DarcsFlag- ( AllowConflicts- , NoAllowConflicts- , MarkConflicts- , SkipConflicts- , SetScriptsExecutable) , externalMerge, allowConflicts- , compression, diffingOpts- , dryRun, reorder, verbosity+ , compress, diffingOpts+ , dryRun, reorder, verbosity, verbose , useCache, wantGuiPause- , umask, toMatchFlags, doReverse- , DarcsFlag(XMLOutput)- , showChangesOnlyToFiles- , diffAlgorithm, maxCount, hasSummary, isInteractive- , selectDeps, hasXmlOutput+ , umask, matchAny, changesReverse+ , onlyToFiles+ , diffAlgorithm, maxCount, isInteractive+ , selectDeps, xmlOutput, hasXmlOutput ) import Darcs.UI.Options- ( DarcsOption, (^), oid, odesc, ocheck, onormalise- , defaultFlags, parseFlags+ ( (^), oid, odesc, ocheck, onormalise+ , defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.UI.PatchHeader ( HijackT, HijackOptions(..), runHijackT@@ -58,18 +53,15 @@ , tentativelyAddToPending, unrecordedChanges, applyToWorking , revertRepositoryChanges , setScriptsExecutablePatches- , listFiles ) import Darcs.Repository.Flags ( UpdateWorking(..), ExternalMerge(..) )-import Darcs.Repository.Internal ( announceMergeConflicts )-import Darcs.Repository.Merge ( tentativelyMergePatches )-import Darcs.Repository.Prefs ( getPreflist )+import Darcs.Repository.Merge ( tentativelyMergePatches, announceMergeConflicts ) import Darcs.Repository.Resolution ( standardResolution ) import Darcs.Patch ( invert, effect, commute, RepoPatch, description ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.Commute ( selfCommuter ) import Darcs.Patch.CommuteFn ( commuterIdFL )-import Darcs.Patch.Info ( showPatchInfo )+import Darcs.Patch.Info ( displayPatchInfo ) import Darcs.Patch.Match ( firstMatch, secondMatch, splitSecondFL ) import Darcs.Patch.Named ( Named, fmapFL_Named, patchcontents, patch2patchinfo ) import Darcs.Patch.Named.Wrapped ( mkRebase, toRebasing, fromRebasing )@@ -116,9 +108,9 @@ import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) import Darcs.Util.English ( englishNum, Noun(Noun) ) import Darcs.Util.Printer- ( vcat, text, ($$)+ ( vcat, text, ($$), redText , putDocLnWith, simplePrinters- , renderString, RenderMode(..)+ , renderString ) import Darcs.Util.Printer.Color ( fancyPrinters ) import Darcs.Util.Progress ( debugMessage )@@ -131,8 +123,6 @@ import Control.Monad.Trans ( liftIO ) import System.Exit ( exitSuccess ) -#include "impossible.h"- rebaseDescription :: String rebaseDescription = "Edit several patches at once." @@ -160,46 +150,6 @@ ] } -suspendBasicOpts :: DarcsOption a- ([O.MatchFlag]- -> O.SelectDeps- -> Maybe Bool- -> Maybe O.Summary- -> O.DiffAlgorithm- -> a)-suspendBasicOpts- = O.matchSeveralOrLast- ^ O.selectDeps- ^ O.interactive- ^ O.summary- ^ O.diffAlgorithm--suspendAdvancedOpts :: DarcsOption a (Bool -> O.UseIndex -> a)-suspendAdvancedOpts- = O.changesReverse- ^ O.useIndex--suspendOpts :: DarcsOption a- ([O.MatchFlag]- -> O.SelectDeps- -> Maybe Bool- -> Maybe O.Summary- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> Bool- -> O.UseIndex- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-suspendOpts = suspendBasicOpts `withStdOpts` suspendAdvancedOpts- suspend :: DarcsCommand [DarcsFlag] suspend = DarcsCommand { commandProgramName = "darcs"@@ -210,7 +160,7 @@ , commandExtraArgs = 0 , commandExtraArgHelp = [] , commandCommand = suspendCmd- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc suspendAdvancedOpts , commandBasicOptions = odesc suspendBasicOpts@@ -218,11 +168,22 @@ , commandCheckOptions = ocheck suspendOpts , commandParseOptions = onormalise suspendOpts }+ where+ suspendBasicOpts+ = O.matchSeveralOrLast+ ^ O.selectDeps+ ^ O.interactive+ ^ O.summary+ ^ O.diffAlgorithm+ suspendAdvancedOpts+ = O.changesReverse+ ^ O.useIndex+ suspendOpts = suspendBasicOpts `withStdOpts` suspendAdvancedOpts suspendCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () suspendCmd _ opts _args =- withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $- StartRebaseJob (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking) $+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $+ StartRebaseJob (RebaseJobFlags (compress ? opts) (verbosity ? opts) YesUpdateWorking) $ \repository -> do allpatches <- readRepo repository (rOld, suspended, allpatches_tail) <- return $ takeHeadRebase allpatches@@ -230,7 +191,7 @@ return $ if firstMatch (parseFlags O.matchSeveralOrLast opts) then getLastPatches (parseFlags O.matchSeveralOrLast opts) allpatches_tail else matchingHead (parseFlags O.matchSeveralOrLast opts) allpatches_tail- let direction = if doReverse opts then Last else LastReversed+ let direction = if changesReverse ? opts then Last else LastReversed patches_context = selectionContext direction "suspend" (patchSelOpts True opts) Nothing Nothing (_ :> psToSuspend) <- runSelection@@ -244,12 +205,12 @@ $ mapM_ (getAuthor "suspend" False Nothing) $ mapFL info psToSuspend repository' <- doSuspend opts repository suspended rOld psToSuspend- finalizeRepositoryChanges repository' YesUpdateWorking (compression opts)+ finalizeRepositoryChanges repository' YesUpdateWorking (compress ? opts) return () doSuspend :: forall p wR wU wT wX- . (RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+ . (RepoPatch p, ApplyState p ~ Tree) => [DarcsFlag] -> Repository ('RepoType 'IsRebase) p wR wU wT -> Suspended p wT wT@@ -257,7 +218,9 @@ -> 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+ pend <- unrecordedChanges (diffingOpts opts)+ O.NoLookForMoves O.NoLookForReplaces+ repository Nothing FlippedSeal psAfterPending <- let effectPsToSuspend = effect psToSuspend in case commute (effectPsToSuspend :> pend) of@@ -270,67 +233,30 @@ case (doPartition invPsEffect pend, doPartition pend invPsEffect) of (_ :> invSuspendedConflicts, _ :> pendConflicts) -> let suspendedConflicts = invert invSuspendedConflicts in- text "these changes in the suspended patches:" $$+ redText "These changes in the suspended patches:" $$ showNicely suspendedConflicts $$- text "conflict with these local changes:" $$+ redText "...conflict with these local changes:" $$ showNicely pendConflicts- fail $ "Can't suspend selected patches without reverting some unrecorded change. Use --verbose to see the details."+ fail $ "Can't suspend selected patches without reverting some unrecorded change."+ ++ if (verbose opts) then "" else " Use --verbose to see the details." rNew <- mkRebase (Items (mapFL_FL (ToEdit . fromRebasing . hopefully) psToSuspend +>+ qs)) invalidateIndex repository- repository' <- tentativelyRemovePatches repository (compression opts) YesUpdateWorking (psToSuspend +>+ (rOld :>: NilFL))+ -- remove the old rebase patch and the patches to suspend+ repository' <- tentativelyRemovePatches repository (compress ? opts) YesUpdateWorking (psToSuspend +>+ (rOld :>: NilFL)) tentativelyAddToPending repository' YesUpdateWorking $ invert $ effect psToSuspend- repository'' <- tentativelyAddPatch repository' (compression opts) (verbosity opts) YesUpdateWorking (n2pia rNew)- _ <- applyToWorking repository'' (verbosity opts) (invert psAfterPending)+ -- add the new rebase patch+ repository'' <- tentativelyAddPatch repository' (compress ? opts) (unVerbose (verbosity ? opts)) YesUpdateWorking (n2pia rNew)+ _ <- applyToWorking repository'' (verbosity ? opts) (invert psAfterPending) `catch` \(e :: IOException) -> fail ("Couldn't undo patch in working dir.\n" ++ show e) return repository'' -unsuspendBasicOpts :: DarcsOption a- (Maybe O.AllowConflicts- -> [O.MatchFlag]- -> Maybe Bool- -> Maybe O.Summary- -> ExternalMerge- -> Bool- -> Maybe String- -> O.DiffAlgorithm- -> a)-unsuspendBasicOpts- = O.conflicts O.YesAllowConflictsAndMark- ^ O.matchSeveralOrFirst- ^ O.interactive- ^ O.summary- ^ O.useExternalMerge- ^ O.keepDate- ^ O.author- ^ O.diffAlgorithm--unsuspendAdvancedOpts :: DarcsOption a (O.UseIndex -> a)-unsuspendAdvancedOpts = O.useIndex--unsuspendOpts :: DarcsOption a- (Maybe O.AllowConflicts- -> [O.MatchFlag]- -> Maybe Bool- -> Maybe O.Summary- -> ExternalMerge- -> Bool- -> Maybe String- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseIndex- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-unsuspendOpts = unsuspendBasicOpts `withStdOpts` unsuspendAdvancedOpts+-- Certain repository functions will display the rebase patch in verbose mode+-- so we use this function to suppress it when passing the verbosity.+unVerbose :: O.Verbosity -> O.Verbosity+unVerbose O.Verbose = O.NormalVerbosity+unVerbose x = x unsuspend :: DarcsCommand [DarcsFlag] unsuspend = DarcsCommand@@ -342,7 +268,7 @@ , commandExtraArgs = 0 , commandExtraArgHelp = [] , commandCommand = unsuspendCmd False- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc unsuspendAdvancedOpts , commandBasicOptions = odesc unsuspendBasicOpts@@ -350,34 +276,18 @@ , commandCheckOptions = ocheck unsuspendOpts , commandParseOptions = onormalise unsuspendOpts }--reifyBasicOpts :: DarcsOption a- ([O.MatchFlag] -> Maybe Bool -> Bool -> Maybe String -> O.DiffAlgorithm -> a)-reifyBasicOpts- = O.matchSeveralOrFirst- ^ O.interactive- ^ O.keepDate- ^ O.author- ^ O.diffAlgorithm--reifyOpts :: DarcsOption a- ([O.MatchFlag]- -> Maybe Bool- -> Bool- -> Maybe String- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-reifyOpts = reifyBasicOpts `withStdOpts` oid+ where+ unsuspendBasicOpts+ = O.conflictsYes+ ^ O.matchSeveralOrFirst+ ^ O.interactive+ ^ O.summary+ ^ O.externalMerge+ ^ O.keepDate+ ^ O.author+ ^ O.diffAlgorithm+ unsuspendAdvancedOpts = O.useIndex+ unsuspendOpts = unsuspendBasicOpts `withStdOpts` unsuspendAdvancedOpts reify :: DarcsCommand [DarcsFlag] reify = DarcsCommand@@ -389,7 +299,7 @@ , commandExtraArgs = 0 , commandExtraArgHelp = [] , commandCommand = unsuspendCmd True- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = odesc reifyBasicOpts@@ -397,14 +307,24 @@ , commandCheckOptions = ocheck reifyOpts , commandParseOptions = onormalise reifyOpts }+ where+ reifyBasicOpts+ = O.matchSeveralOrFirst+ ^ O.interactive+ ^ O.keepDate+ ^ O.author+ ^ O.diffAlgorithm+ reifyOpts = reifyBasicOpts `withStdOpts` oid unsuspendCmd :: Bool -> (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () unsuspendCmd reifyFixups _ opts _args =- withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $- RebaseJob (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking) $+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RebaseJob (RebaseJobFlags (compress ? opts) (verbosity ? opts) YesUpdateWorking) $ \(repository :: Repository ('RepoType 'IsRebase) p wR wU wR) -> (do patches <- readRepo repository- pend <- unrecordedChanges (diffingOpts opts) repository Nothing+ pend <- unrecordedChanges (diffingOpts opts)+ O.NoLookForMoves O.NoLookForReplaces+ 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"@@ -413,7 +333,7 @@ let selects = toRebaseSelect ps - let matchFlags = toMatchFlags opts+ let matchFlags = matchAny ? opts inRange :> outOfRange <- return $ if secondMatch matchFlags then@@ -422,9 +342,9 @@ offer :> dontoffer <- return $- if SkipConflicts `elem` opts- then partitionUnconflicted inRange- else inRange :> NilRL+ case O.conflictsYes ? opts of+ Nothing -> partitionUnconflicted inRange -- skip conflicts+ Just _ -> inRange :> NilRL let warnSkip :: RL q wX wY -> IO () warnSkip NilRL = return ()@@ -440,7 +360,7 @@ (ps_to_unsuspend :: FL (WDDNamed p) wR wZ) :> chosen_fixups <- (if reifyFixups then reifyRebaseSelect else return . extractRebaseSelect) chosen - let da = diffAlgorithm opts+ let da = diffAlgorithm ? opts ps_to_keep = simplifyPushes da chosen_fixups . fromRebaseSelect $ keep +>+ reverseRL dontoffer +>+ outOfRange@@ -450,30 +370,29 @@ $ mapFL_FL (patchcontents . wddPatch) ps_to_unsuspend :: IO (Sealed (FL (PrimOf p) wZ)) - let merge_opts | NoAllowConflicts `elem` opts = opts- | AllowConflicts `elem` opts = opts- | otherwise = MarkConflicts : opts-- have_conflicts <- announceMergeConflicts "unsuspend" (allowConflicts merge_opts) (externalMerge merge_opts) standard_resolved_p+ have_conflicts <- announceMergeConflicts "unsuspend"+ (allowConflicts opts) (externalMerge ? opts) standard_resolved_p Sealed (resolved_p :: FL (PrimOf p) wA wB) <-- case (externalMerge opts, have_conflicts) of- (NoExternalMerge,_) -> return $ if AllowConflicts `elem` opts -- i.e. don't mark them- then seal NilFL- else seal standard_resolved_p- (_,False) -> return $ seal standard_resolved_p- (YesExternalMerge _, True) -> error "external resolution for unsuspend not implemented yet"+ case (externalMerge ? opts, have_conflicts) of+ (NoExternalMerge, _) ->+ case O.conflictsYes ? opts of+ Just O.YesAllowConflicts -> return $ seal NilFL -- i.e. don't mark them+ _ -> return $ seal standard_resolved_p+ (_, False) -> return $ seal standard_resolved_p+ (YesExternalMerge _, True) ->+ error "external resolution for unsuspend not implemented yet" let effect_to_apply = concatFL (mapFL_FL effect ps_to_unsuspend) +>+ resolved_p invalidateIndex repository- repository' <- tentativelyRemovePatches repository (compression opts) YesUpdateWorking (rOld :>: NilFL)+ repository' <- tentativelyRemovePatches repository (compress ? 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 -- 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) ->+ repository''' <- tentativelyAddPatch repository'' (compress ? opts) (verbosity ? opts) YesUpdateWorking (n2pia rNew)+ finalizeRepositoryChanges repository''' YesUpdateWorking (compress ? opts)+ _ <- applyToWorking repository''' (verbosity ? opts) effect_to_apply `catch` \(e :: IOException) -> fail ("couldn't apply patch in working dir.\n" ++ show e) return () ) :: IO ()@@ -493,8 +412,8 @@ ++ englishNum (length deps) (Noun "dependency") ":\n\n" let printIndented n = mapM_ (putStrLn . (replicate n ' '++)) . lines .- renderString Encode . showPatchInfo- putStrLn . renderString Encode . showPatchInfo .+ renderString . displayPatchInfo+ putStrLn . renderString . displayPatchInfo . patch2patchinfo $ wddPatch p putStr " depended on:\n" mapM_ (printIndented 2) deps@@ -504,14 +423,14 @@ p' <- snd <$> updatePatchHeader "unsuspend" NoAskAboutDeps (patchSelOpts True opts)- (diffAlgorithm opts)+ (diffAlgorithm ? opts) (parseFlags O.keepDate opts) (parseFlags O.selectAuthor opts) (parseFlags O.author opts) (parseFlags O.patchname opts) (parseFlags O.askLongComment opts) (n2pia (toRebasing (wddPatch p))) NilFL- repo' <- liftIO $ tentativelyAddPatch repo (compression opts) (verbosity opts) YesUpdateWorking p'+ repo' <- liftIO $ tentativelyAddPatch repo (compress ? 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))@@ -524,26 +443,6 @@ return (repo'', rename2 :>: renames) -injectBasicOpts :: DarcsOption a (Bool -> Maybe String -> O.DiffAlgorithm -> a)-injectBasicOpts = O.keepDate ^ O.author ^ O.diffAlgorithm--injectOpts :: DarcsOption a - (Bool- -> Maybe String- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-injectOpts = injectBasicOpts `withStdOpts` oid- inject :: DarcsCommand [DarcsFlag] inject = DarcsCommand { commandProgramName = "darcs"@@ -554,7 +453,7 @@ , commandExtraArgs = 0 , commandExtraArgHelp = [] , commandCommand = injectCmd- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = odesc injectBasicOpts@@ -562,11 +461,14 @@ , commandCheckOptions = ocheck injectOpts , commandParseOptions = onormalise injectOpts }+ where+ injectBasicOpts = O.keepDate ^ O.author ^ O.diffAlgorithm+ injectOpts = injectBasicOpts `withStdOpts` oid injectCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () injectCmd _ opts _args =- withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $- RebaseJob (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking) $+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RebaseJob (RebaseJobFlags (compress ? opts) (verbosity ? opts) YesUpdateWorking) $ \(repository :: Repository ('RepoType 'IsRebase) p wR wU wR) -> do patches <- readRepo repository @@ -588,7 +490,7 @@ name_fixups :> prim_fixups <- return $ flToNamesPrims fixups - let changes_context = selectionContextPrim Last "inject" (patchSelOpts True opts) (Just (primSplitter (diffAlgorithm opts))) Nothing Nothing+ 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@@ -596,36 +498,18 @@ exitSuccess -- Don't bother to update patch header since unsuspend will do that later- let da = diffAlgorithm opts+ let da = diffAlgorithm ? opts toeditNew = fmapFL_Named (mapFL_FL fromPrim . canonizeFL da . (injects +>+) . effect) toedit rNew <- unseal (mkRebase . Items) $ unseal (simplifyPushes da (mapFL_FL NameFixup name_fixups)) $ simplifyPushes da (mapFL_FL PrimFixup rest_fixups) $ ToEdit toeditNew :>: fromRebaseSelect rest_selects - repository' <- tentativelyRemovePatches repository (compression opts) YesUpdateWorking (rOld :>: NilFL)- repository'' <- tentativelyAddPatch repository' (compression opts) (verbosity opts) YesUpdateWorking (n2pia rNew)- finalizeRepositoryChanges repository'' YesUpdateWorking (compression opts)+ repository' <- tentativelyRemovePatches repository (compress ? opts) YesUpdateWorking (rOld :>: NilFL)+ repository'' <- tentativelyAddPatch repository' (compress ? opts) (verbosity ? opts) YesUpdateWorking (n2pia rNew)+ finalizeRepositoryChanges repository'' YesUpdateWorking (compress ? opts) return () -obliterateBasicOpts :: DarcsOption a (O.DiffAlgorithm -> a)-obliterateBasicOpts = O.diffAlgorithm--obliterateOpts :: DarcsOption a- (O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-obliterateOpts = obliterateBasicOpts `withStdOpts` oid- obliterate :: DarcsCommand [DarcsFlag] obliterate = DarcsCommand { commandProgramName = "darcs"@@ -636,7 +520,7 @@ , commandExtraArgs = 0 , commandExtraArgHelp = [] , commandCommand = obliterateCmd- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = odesc obliterateBasicOpts@@ -644,11 +528,14 @@ , commandCheckOptions = ocheck obliterateOpts , commandParseOptions = onormalise obliterateOpts }+ where+ obliterateBasicOpts = O.diffAlgorithm+ obliterateOpts = obliterateBasicOpts `withStdOpts` oid obliterateCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () obliterateCmd _ opts _args =- withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $- RebaseJob (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking) $+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RebaseJob (RebaseJobFlags (compress ? opts) (verbosity ? opts) YesUpdateWorking) $ \(repository :: Repository ('RepoType 'IsRebase) p wR wU wR) -> (do patches <- readRepo repository @@ -662,7 +549,7 @@ when (nullFL chosen) $ do putStrLn "No patches selected!" exitSuccess - let da = diffAlgorithm opts+ let da = diffAlgorithm ? opts do_obliterate :: FL (RebaseItem p) wX wY -> FL (RebaseItem p) wY wZ -> Sealed (FL (RebaseItem p) wX) do_obliterate NilFL = Sealed do_obliterate (Fixup f :>: qs) = unseal (simplifyPush da f) . do_obliterate qs@@ -675,9 +562,9 @@ let ps_to_keep = do_obliterate (fromRebaseSelect chosen) (fromRebaseSelect 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)- finalizeRepositoryChanges repository'' YesUpdateWorking (compression opts)+ repository' <- tentativelyRemovePatches repository (compress ? opts) YesUpdateWorking (rOld :>: NilFL)+ repository'' <- tentativelyAddPatch repository' (compress ? opts) (verbosity ? opts) YesUpdateWorking (n2pia rNew)+ finalizeRepositoryChanges repository'' YesUpdateWorking (compress ? opts) return () ) :: IO () @@ -690,96 +577,6 @@ pullHelp = "Copy and apply patches from another repository, suspending any local patches that conflict." -pullBasicOpts :: DarcsOption a- ([O.MatchFlag]- -> O.Reorder- -> Maybe Bool- -> Maybe O.AllowConflicts- -> ExternalMerge- -> O.RunTest- -> O.DryRun- -> O.XmlOutput- -> Maybe O.Summary- -> O.SelectDeps- -> Maybe Bool- -> Maybe String- -> Bool- -> O.DiffAlgorithm- -> a)-pullBasicOpts- = O.matchSeveral- ^ O.reorder- ^ O.interactive- ^ O.conflicts O.YesAllowConflictsAndMark- ^ O.useExternalMerge- ^ O.test- ^ O.dryRunXml- ^ O.summary- ^ O.selectDeps- ^ O.setDefault- ^ O.workingRepoDir- ^ O.allowUnrelatedRepos- ^ O.diffAlgorithm--pullAdvancedOpts :: DarcsOption a- (O.RepoCombinator- -> O.Compression- -> O.UseIndex- -> O.RemoteRepos- -> O.SetScriptsExecutable- -> O.UMask- -> Bool- -> Bool- -> O.NetworkOptions- -> a)-pullAdvancedOpts- = O.repoCombinator- ^ O.compress- ^ O.useIndex- ^ O.remoteRepos- ^ O.setScriptsExecutable- ^ O.umask- ^ O.restrictPaths- ^ O.changesReverse- ^ O.network--pullOpts :: DarcsOption a- ([O.MatchFlag]- -> O.Reorder- -> Maybe Bool- -> Maybe O.AllowConflicts- -> ExternalMerge- -> O.RunTest- -> O.DryRun- -> O.XmlOutput- -> Maybe O.Summary- -> O.SelectDeps- -> Maybe Bool- -> Maybe String- -> Bool- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.RepoCombinator- -> O.Compression- -> O.UseIndex- -> O.RemoteRepos- -> O.SetScriptsExecutable- -> O.UMask- -> Bool- -> Bool- -> O.NetworkOptions- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-pullOpts = pullBasicOpts `withStdOpts` pullAdvancedOpts- pull :: DarcsCommand [DarcsFlag] pull = DarcsCommand { commandProgramName = "darcs"@@ -790,7 +587,7 @@ , commandExtraArgHelp = ["[REPOSITORY]..."] , commandCommand = pullCmd RebasePatchApplier , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = getPreflist "repos"+ , commandCompleteArgs = prefArgs "repos" , commandArgdefaults = defaultRepo , commandAdvancedOptions = odesc pullAdvancedOpts , commandBasicOptions = odesc pullBasicOpts@@ -798,6 +595,32 @@ , commandCheckOptions = ocheck pullOpts , commandParseOptions = onormalise pullOpts }+ where+ pullBasicOpts+ = O.matchSeveral+ ^ O.reorder+ ^ O.interactive+ ^ O.conflictsYes+ ^ O.externalMerge+ ^ O.runTest+ ^ O.dryRunXml+ ^ O.summary+ ^ O.selectDeps+ ^ O.setDefault+ ^ O.repoDir+ ^ O.allowUnrelatedRepos+ ^ O.diffAlgorithm+ pullAdvancedOpts+ = O.repoCombinator+ ^ O.compress+ ^ O.useIndex+ ^ O.remoteRepos+ ^ O.setScriptsExecutable+ ^ O.umask+ ^ O.restrictPaths+ ^ O.changesReverse+ ^ O.network+ pullOpts = pullBasicOpts `withStdOpts` pullAdvancedOpts applyDescription :: String applyDescription = "Apply a patch bundle, suspending any local patches that conflict."@@ -819,7 +642,7 @@ , commandExtraArgHelp = ["<PATCHFILE>"] , commandCommand = applyCmd RebasePatchApplier , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = listFiles False+ , commandCompleteArgs = fileArgs , commandArgdefaults = const stdindefault , commandAdvancedOptions = odesc applyAdvancedOpts , commandBasicOptions = odesc applyBasicOpts@@ -827,85 +650,28 @@ , commandCheckOptions = ocheck applyOpts , commandParseOptions = onormalise applyOpts }--applyBasicOpts :: DarcsOption a- (O.Verify- -> O.Reorder- -> Maybe Bool- -> O.DryRun- -> O.XmlOutput- -> [O.MatchFlag]- -> Maybe String- -> O.DiffAlgorithm- -> a)-applyBasicOpts- = O.verify- ^ O.reorder- ^ O.interactive- ^ O.dryRunXml- ^ O.matchSeveral- ^ O.workingRepoDir- ^ O.diffAlgorithm--applyAdvancedOpts :: DarcsOption a- (Maybe String- -> Maybe String- -> Bool- -> (Bool, Maybe String)- -> O.UseIndex- -> O.Compression- -> O.SetScriptsExecutable- -> O.UMask- -> Bool- -> Bool- -> O.WantGuiPause- -> a)-applyAdvancedOpts- = O.reply- ^ O.ccApply- ^ O.happyForwarding- ^ O.sendmail- ^ O.useIndex- ^ O.compress- ^ O.setScriptsExecutable- ^ O.umask- ^ O.restrictPaths- ^ O.changesReverse- ^ O.pauseForGui--applyOpts :: DarcsOption a- (O.Verify- -> O.Reorder- -> Maybe Bool- -> O.DryRun- -> O.XmlOutput- -> [O.MatchFlag]- -> Maybe String- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> Maybe String- -> Maybe String- -> Bool- -> (Bool, Maybe String)- -> O.UseIndex- -> O.Compression- -> O.SetScriptsExecutable- -> O.UMask- -> Bool- -> Bool- -> O.WantGuiPause- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)--applyOpts = applyBasicOpts `withStdOpts` applyAdvancedOpts+ where+ applyBasicOpts+ = O.verify+ ^ O.reorder+ ^ O.interactive+ ^ O.dryRunXml+ ^ O.matchSeveral+ ^ O.repoDir+ ^ O.diffAlgorithm+ applyAdvancedOpts+ = O.reply+ ^ O.ccApply+ ^ O.happyForwarding+ ^ O.sendmail+ ^ O.useIndex+ ^ O.compress+ ^ O.setScriptsExecutable+ ^ O.umask+ ^ O.restrictPaths+ ^ O.changesReverse+ ^ O.pauseForGui+ applyOpts = applyBasicOpts `withStdOpts` applyAdvancedOpts data RebasePatchApplier = RebasePatchApplier @@ -914,14 +680,13 @@ repoJob RebasePatchApplier opts f = StartRebaseJob- (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking)+ (RebaseJobFlags (compress ? 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 ) => String -> [DarcsFlag] -> String@@ -931,10 +696,10 @@ -> IO () applyPatchesForRebaseCmd cmdName opts _from_whom repository us' to_be_applied = do printDryRunMessageAndExit cmdName- (verbosity opts)- (hasSummary O.NoSummary opts)- (dryRun opts)- (hasXmlOutput opts)+ (verbosity ? opts)+ (O.summary ? opts)+ (dryRun ? opts)+ (xmlOutput ? opts) (isInteractive True opts) to_be_applied setEnvDarcsPatches to_be_applied@@ -962,27 +727,30 @@ (rOld, suspended, _) <- return $ takeHeadRebaseFL us' repository' <- doSuspend opts repository suspended rOld usToSuspend+ -- the new rebase patch containing the suspended patches is now in the repo+ -- and the suspended patches have been removed -- 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 -- the right thing.- finalizeRepositoryChanges repository' YesUpdateWorking (compression opts)+ finalizeRepositoryChanges repository' YesUpdateWorking (compress ? opts) >> revertRepositoryChanges repository' YesUpdateWorking Sealed pw <- tentativelyMergePatches repository' cmdName (allowConflicts opts) YesUpdateWorking- (externalMerge opts)- (wantGuiPause opts) (compression opts) (verbosity opts)- (reorder opts) (diffingOpts opts)+ (externalMerge ? opts)+ (wantGuiPause opts) (compress ? opts) (verbosity ? opts)+ (reorder ? opts) (diffingOpts opts) (usOk +>+ usKeep) to_be_applied invalidateIndex repository- finalizeRepositoryChanges repository' YesUpdateWorking (compression opts)- _ <- revertable $ applyToWorking repository' (verbosity opts) pw- when (SetScriptsExecutable `elem` opts) $ setScriptsExecutablePatches pw+ finalizeRepositoryChanges repository' YesUpdateWorking (compress ? opts)+ _ <- revertable $ applyToWorking repository' (verbosity ? opts) pw+ when (O.setScriptsExecutable ? opts == O.YesSetScriptsExecutable) $+ setScriptsExecutablePatches pw putInfo opts $ text $ "Finished " ++ cmdName ++ "ing." -- TODO I doubt this is right, e.g. withContext should be inherited@@ -1003,11 +771,11 @@ patchSelOpts :: Bool -> [DarcsFlag] -> S.PatchSelectionOptions patchSelOpts defInteractive flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = parseFlags O.matchSeveralOrLast flags , S.interactive = isInteractive defInteractive flags- , S.selectDeps = selectDeps flags- , S.summary = hasSummary O.NoSummary flags+ , S.selectDeps = selectDeps ? flags+ , S.summary = O.summary ? flags , S.withContext = O.NoContext } @@ -1021,7 +789,7 @@ , commandExtraArgs = 0 , commandExtraArgHelp = [] , commandCommand = logCmd- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc logAdvancedOpts , commandBasicOptions = odesc logBasicOpts@@ -1029,39 +797,15 @@ , commandCheckOptions = ocheck logOpts , commandParseOptions = onormalise logOpts }--logBasicOpts :: DarcsOption a- (Maybe O.Summary- -> Maybe Bool- -> a)-logBasicOpts- = O.summary- ^ O.interactive -- False--logAdvancedOpts :: DarcsOption a a-logAdvancedOpts = oid--logOpts :: DarcsOption a- (Maybe O.Summary- -> Maybe Bool- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)--logOpts = logBasicOpts `withStdOpts` logAdvancedOpts+ where+ logBasicOpts = O.summary ^ O.interactive -- False+ logAdvancedOpts = oid+ logOpts = logBasicOpts `withStdOpts` logAdvancedOpts logCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () logCmd _ opts _files =- withRepository (useCache opts) $- RebaseJob (RebaseJobFlags (compression opts) (verbosity opts) YesUpdateWorking) $ \repository -> do+ withRepository (useCache ? opts) $+ RebaseJob (RebaseJobFlags (compress ? opts) (verbosity ? opts) YesUpdateWorking) $ \repository -> do patches <- readRepo repository (_, Items ps, _) <- return $ takeHeadRebase patches let psToShow = toRebaseChanges ps@@ -1069,14 +813,14 @@ then viewChanges (patchSelOpts False opts) (mapFL Sealed2 psToShow) else do debugMessage "About to print the changes..."- let printers = if XMLOutput `elem` opts then simplePrinters else fancyPrinters+ let printers = if hasXmlOutput opts then simplePrinters else fancyPrinters emptyPatchSet = PatchSet NilRL NilRL patchSet = appendPSFL emptyPatchSet psToShow logInfo <- getLogInfo- (maxCount opts)- (toMatchFlags opts)- (showChangesOnlyToFiles opts)+ (maxCount ? opts)+ (matchAny ? opts)+ (onlyToFiles ? opts) Nothing (\_ qs -> return qs) patchSet
@@ -15,7 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module Darcs.UI.Commands.Record@@ -43,16 +42,13 @@ , tentativelyAddPatch , finalizeRepositoryChanges , invalidateIndex- , unrecordedChangesWithPatches+ , unrecordedChanges , readRecorded- , listRegisteredFiles ) 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 )-import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) import Darcs.Patch.Info ( PatchInfo ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Split ( primSplitter )@@ -74,6 +70,7 @@ ) import Darcs.UI.Commands.Util ( announceFiles, filterExistingPaths, testTentativeAndMaybeExit )+import Darcs.UI.Completion ( modifiedFileArgs ) import Darcs.UI.Flags ( DarcsFlag , fileHelpAuthor@@ -87,7 +84,6 @@ import Darcs.UI.PatchHeader ( getLog ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.Flags ( UpdateWorking (..), DryRun(NoDryRun), ScanKnown(..) )-import Darcs.Repository.State ( getMovesPs, getReplaces ) import Darcs.Util.Exception ( clarifyErrors ) import Darcs.Util.Prompt ( promptYorn ) import Darcs.Util.Progress ( debugMessage )@@ -127,10 +123,10 @@ ^ O.testChanges ^ O.interactive ^ O.pipe- ^ O.askdeps+ ^ O.askDeps ^ O.askLongComment ^ O.lookfor- ^ O.workingRepoDir+ ^ O.repoDir ^ O.withContext ^ O.diffAlgorithm @@ -138,37 +134,6 @@ (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- -> Maybe String- -> O.TestChanges- -> Maybe Bool- -> Bool- -> Bool- -> Maybe O.AskLongComment- -> O.LookFor- -> Maybe String- -> O.WithContext- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.Logfile- -> O.Compression- -> O.UseIndex- -> O.UMask- -> O.SetScriptsExecutable- -> O.IncludeBoring- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-recordOpts = recordBasicOpts `withStdOpts` recordAdvancedOpts- data RecordConfig = RecordConfig { patchname :: Maybe String , author :: Maybe String@@ -204,14 +169,16 @@ , commandExtraArgHelp = ["[FILE or DIRECTORY]..."] , commandCommand = recordCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = listRegisteredFiles+ , commandCompleteArgs = modifiedFileArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc recordAdvancedOpts , commandBasicOptions = odesc recordBasicOpts , commandDefaults = defaultFlags recordOpts , commandCheckOptions = ocheck recordOpts , commandParseOptions = recordConfig-}+ }+ where+ recordOpts = recordBasicOpts `withStdOpts` recordAdvancedOpts -- | commit is an alias for record commit :: DarcsCommand RecordConfig@@ -232,7 +199,11 @@ files <- if null args then return Nothing else Just . sort <$> fixSubPaths fps args when (files == Just []) $ fail "No valid arguments were given."- files' <- traverse (filterExistingPaths repository (verbosity cfg) (useIndex cfg) scan) files+ files' <-+ traverse+ (filterExistingPaths+ repository (verbosity cfg) (useIndex cfg) scan (O.moves (lookfor cfg)))+ files when (verbosity cfg /= O.Quiet) $ traverse_ (reportNonExisting scan) files' let files'' = fmap snd files'@@ -241,15 +212,9 @@ return files'' 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 existing_files- else return (Sealed NilFL)- movesPs <- if O.moves (lookfor cfg) == O.YesLookForMoves- then getMovesPs repository existing_files- else return NilFL- changes <- unrecordedChangesWithPatches- movesPs (unsafeCoerceP replacePs :: FL (PrimOf p) wR wR)- (diffingOpts cfg) repository existing_files+ changes <- unrecordedChanges (diffingOpts cfg)+ (O.moves (lookfor cfg)) (O.replaces (lookfor cfg))+ repository existing_files debugMessage "I've got unrecorded changes." case changes of NilFL | not (askDeps cfg) -> do@@ -270,7 +235,7 @@ 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 :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+doRecord :: (IsRepoType rt, RepoPatch p, ApplyState 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)@@ -361,7 +326,7 @@ , "record failed and left a `_darcs/patch_description.txt` file." , "" , unlines fileHelpAuthor- , "If you want to manually define any extra dependencies for your patch,"+ , "If you want to manually define any explicit dependencies for your patch," , "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."
@@ -15,25 +15,24 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.UI.Commands.Remove ( remove, rm, unadd ) where import Prelude () import Darcs.Prelude -import Prelude hiding ( (^) )- import Control.Monad ( when, foldM )-import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults,- commandAlias, commandStub,- putWarning- , amInHashedRepository- )-import Darcs.UI.Commands.Add( expandDirs )+import Darcs.UI.Commands+ ( DarcsCommand(..)+ , withStdOpts, nodefaults+ , commandAlias, commandStub+ , putWarning, putInfo+ , amInHashedRepository+ )+import Darcs.UI.Commands.Util ( expandDirs )+import Darcs.UI.Completion ( knownFileArgs ) import Darcs.UI.Flags- ( DarcsFlag, useCache, dryRun, umask, diffAlgorithm, fixSubPaths, verbosity )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags )+ ( DarcsFlag, useCache, dryRun, umask, diffAlgorithm, fixSubPaths, quiet )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.Flags ( UpdateWorking (..) ) import Darcs.Repository@@ -43,7 +42,6 @@ , addToPending , readRecordedAndPending , readUnrecorded- , listRegisteredFiles ) import Darcs.Repository.Diff( treeDiff ) import Darcs.Patch ( RepoPatch, PrimOf, PrimPatch, adddir, rmdir, addfile, rmfile,@@ -55,8 +53,7 @@ import Darcs.Util.Tree( Tree, TreeItem(..), find, modifyTree, expand, list ) import Darcs.Util.Path( anchorPath, AnchoredPath, fn2fp, SubPath, sp2fn , AbsolutePath, floatPath )-import Darcs.Util.Printer ( text )-+import Darcs.Util.Printer ( text, vcat ) removeDescription :: String removeDescription = "Remove files from version control."@@ -72,29 +69,6 @@ "Note that applying a removal patch to a repository (e.g. by pulling\n" ++ "the patch) will ALWAYS affect the working tree of that repository.\n" -removeBasicOpts :: DarcsOption a (Maybe String -> Bool -> a)-removeBasicOpts = O.workingRepoDir ^ O.recursive--removeAdvancedOpts :: DarcsOption a (O.UMask -> a)-removeAdvancedOpts = O.umask--removeOpts :: DarcsOption a- (Maybe String- -> Bool- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-removeOpts = removeBasicOpts `withStdOpts` removeAdvancedOpts- remove :: DarcsCommand [DarcsFlag] remove = DarcsCommand { commandProgramName = "darcs"@@ -105,7 +79,7 @@ , commandExtraArgHelp = ["<FILE or DIRECTORY> ..."] , commandCommand = removeCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = listRegisteredFiles+ , commandCompleteArgs = knownFileArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc removeAdvancedOpts , commandBasicOptions = odesc removeBasicOpts@@ -113,6 +87,10 @@ , commandCheckOptions = ocheck removeOpts , commandParseOptions = onormalise removeOpts }+ where+ removeBasicOpts = O.repoDir ^ O.recursive+ removeAdvancedOpts = O.umask+ removeOpts = removeBasicOpts `withStdOpts` removeAdvancedOpts removeCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () removeCmd fps opts relargs = do@@ -121,17 +99,17 @@ origfiles <- fixSubPaths fps relargs when (null origfiles) $ fail "No valid arguments were given."- withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $+ RepoJob $ \repository -> do args <- if parseFlags O.recursive opts then reverse `fmap` expandDirs False origfiles else return origfiles Sealed p <- makeRemovePatch opts repository args- let notQuiet = verbosity opts /= O.Quiet- when (nullFL p && not (null origfiles) && notQuiet) $+ -- TODO whether command fails depends on verbosity BAD BAD BAD+ when (nullFL p && not (null origfiles) && not (quiet opts)) $ fail "No files were removed." addToPending repository YesUpdateWorking p- when notQuiet $- putStr $ unlines $ ["Will stop tracking:"] ++ listTouchedFiles p+ putInfo opts $ vcat $ map text $ ["Will stop tracking:"] ++ listTouchedFiles p -- | makeRemovePatch builds a list of patches to remove the given filepaths. -- This function does not recursively process directories. The 'Recursive'@@ -176,7 +154,7 @@ then skipAndWarn "it is not empty" else return $ Just $ freeGap (rmdir f_fp :>: NilFL) (Just (File _), Just (File _)) -> do- Just `fmap` treeDiff (diffAlgorithm opts) ftf unrecorded unrecorded'+ Just `fmap` treeDiff (diffAlgorithm ? opts) ftf unrecorded unrecorded' (Just (File _), _) -> return $ Just $ freeGap (addfile f_fp :>: rmfile f_fp :>: NilFL) (Just (SubTree _), _) ->
@@ -15,11 +15,8 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.UI.Commands.Repair- (- repair- , check- ) where+{-# LANGUAGE RecordWildCards #-}+module Darcs.UI.Commands.Repair ( repair, check ) where import Prelude () import Darcs.Prelude@@ -30,29 +27,39 @@ import System.Directory( renameFile ) import System.FilePath ( (</>) ) -import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, putInfo, commandAlias, amInHashedRepository )-import Darcs.UI.Flags as F- ( DarcsFlag(Quiet,DryRun)- , verbosity, dryRun, umask, useIndex- , useCache, compression, diffAlgorithm )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Commands+ ( DarcsCommand(..), withStdOpts, nodefaults+ , putInfo, amInHashedRepository+ )+import Darcs.UI.Completion ( noArgs )+import Darcs.UI.Flags+ ( DarcsFlag, verbosity, dryRun, umask, useIndex+ , useCache, compress, diffAlgorithm, quiet+ )+import Darcs.UI.Options+ ( DarcsOption, (^), oid+ , odesc, ocheck, onormalise, defaultFlags, (?)+ ) import qualified Darcs.UI.Options.All as O+ import Darcs.Repository.Flags ( UpdateWorking (..) )-import Darcs.Repository.Repair( replayRepository, checkIndex,- replayRepositoryInTemp,- RepositoryConsistency(..) )-import Darcs.Repository ( Repository, withRepository,- readRecorded, RepoJob(..),- withRepoLock, replacePristine, writePatchSet )-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 )+import Darcs.Repository.Repair+ ( replayRepository, checkIndex, replayRepositoryInTemp+ , RepositoryConsistency(..)+ )+import Darcs.Repository+ ( Repository, withRepository, readRecorded, RepoJob(..)+ , withRepoLock, replacePristine, writePatchSet+ ) import Darcs.Repository.Prefs ( filetypeFunction ) import Darcs.Repository.Diff( treeDiff )++import Darcs.Patch ( RepoPatch, showNicely, PrimOf )+import Darcs.Patch.Witnesses.Ordered ( FL(..) )+import Darcs.Patch.Witnesses.Sealed ( Sealed(..), unFreeLeft )+ import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Printer ( text, ($$), (<+>) )-import Darcs.Util.Path ( AbsolutePath ) import Darcs.Util.Tree( Tree ) @@ -71,31 +78,9 @@ "unsuccessfully (with a non-zero exit status) if the rebuilt pristine is\n" ++ "different from the current pristine.\n" -repairBasicOpts :: DarcsOption a- (Maybe String -> O.UseIndex -> O.DryRun -> O.DiffAlgorithm -> a)-repairBasicOpts = O.workingRepoDir ^ O.useIndex ^ O.dryRun ^ O.diffAlgorithm--repairAdvancedOpts :: DarcsOption a (O.UMask -> a)-repairAdvancedOpts = O.umask--repairOpts :: DarcsOption a- (Maybe String- -> O.UseIndex- -> O.DryRun- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-repairOpts = repairBasicOpts `withStdOpts` repairAdvancedOpts+commonBasicOpts :: DarcsOption a+ (Maybe String -> O.UseIndex -> O.DiffAlgorithm -> a)+commonBasicOpts = O.repoDir ^ O.useIndex ^ O.diffAlgorithm repair :: DarcsCommand [DarcsFlag] repair = DarcsCommand@@ -105,82 +90,108 @@ , commandDescription = repairDescription , commandExtraArgs = 0 , commandExtraArgHelp = []- , commandCommand = repairCmd+ , commandCommand = withFpsAndArgs repairCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults- , commandAdvancedOptions = odesc repairAdvancedOpts- , commandBasicOptions = odesc repairBasicOpts- , commandDefaults = defaultFlags repairOpts- , commandCheckOptions = ocheck repairOpts- , commandParseOptions = onormalise repairOpts+ , .. }+ where+ basicOpts = commonBasicOpts ^ O.dryRun+ advancedOpts = O.umask+ allOpts = basicOpts `withStdOpts` advancedOpts+ commandAdvancedOptions = odesc advancedOpts+ commandBasicOptions = odesc basicOpts+ commandDefaults = defaultFlags allOpts+ commandCheckOptions = ocheck allOpts+ commandParseOptions = onormalise allOpts -repairCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-repairCmd _ opts _- | DryRun `elem` opts = withRepository (useCache opts) (RepoJob (check' opts))- | otherwise = withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do- replayRepository (F.diffAlgorithm opts) repository (compression opts) (verbosity opts) $ \state ->- case state of- RepositoryConsistent ->- putStrLn "The repository is already consistent, no changes made."- BrokenPristine tree -> do- putStrLn "Fixing pristine tree..."- replacePristine repository tree- BrokenPatches tree newps -> do- putStrLn "Writing out repaired patches..."- _ <- writePatchSet newps (useCache opts)- putStrLn "Fixing pristine tree..."- replacePristine repository tree- index_ok <- checkIndex repository (Quiet `elem` opts)- unless index_ok $ do renameFile (darcsdir </> "index") (darcsdir </> "index.bad")- putStrLn "Bad index discarded."+withFpsAndArgs :: (b -> d) -> a -> b -> c -> d+withFpsAndArgs cmd _ opts _ = cmd opts -check'- :: 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)+repairCmd :: [DarcsFlag] -> IO ()+repairCmd opts = case dryRun ? opts of+ O.YesDryRun -> checkCmd opts+ O.NoDryRun ->+ withRepoLock O.NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts)+ $ RepoJob $ \repository -> do+ replayRepository (diffAlgorithm ? opts) repository (compress ? opts) (verbosity ? opts) $ \state ->+ case state of+ RepositoryConsistent ->+ putStrLn "The repository is already consistent, no changes made."+ BrokenPristine tree -> do+ putStrLn "Fixing pristine tree..."+ replacePristine repository tree+ BrokenPatches tree newps -> do+ putStrLn "Writing out repaired patches..."+ _ <- writePatchSet newps (useCache ? opts)+ putStrLn "Fixing pristine tree..."+ replacePristine repository tree+ index_ok <- checkIndex repository (quiet opts)+ unless index_ok $ do renameFile (darcsdir </> "index") (darcsdir </> "index.bad")+ putStrLn "Bad index discarded."++-- |check is an alias for repair, with implicit DryRun flag.+check :: DarcsCommand [DarcsFlag]+check = DarcsCommand+ { commandProgramName = "darcs"+ , commandName = "check"+ , commandHelp = "See `darcs repair` for details."+ , commandExtraArgs = 0+ , commandExtraArgHelp = []+ , commandCommand = withFpsAndArgs checkCmd+ , commandPrereq = amInHashedRepository+ , commandCompleteArgs = noArgs+ , commandArgdefaults = nodefaults+ , ..+ }+ where+ basicOpts = commonBasicOpts+ advancedOpts = oid+ allOpts = basicOpts `withStdOpts` advancedOpts+ commandAdvancedOptions = odesc advancedOpts+ commandBasicOptions = odesc basicOpts+ commandDefaults = defaultFlags allOpts+ commandCheckOptions = ocheck allOpts+ commandParseOptions = onormalise allOpts+ commandDescription = "Alias for `darcs " ++ commandName repair ++ " --dry-run'."++checkCmd :: [DarcsFlag] -> IO ()+checkCmd opts = withRepository (useCache ? opts) $ RepoJob $ \repository -> do+ state <- replayRepositoryInTemp (diffAlgorithm ? opts) repository (compress ? opts) (verbosity ? opts) failed <- case state of RepositoryConsistent -> do putInfo opts $ text "The repository is consistent!" return False BrokenPristine newpris -> do- brokenPristine newpris+ brokenPristine opts repository newpris return True BrokenPatches newpris _ -> do- brokenPristine newpris+ brokenPristine opts repository newpris putInfo opts $ text "Found broken patches." return True- bad_index <- if useIndex opts == O.IgnoreIndex+ bad_index <- if useIndex ? opts == O.IgnoreIndex then return False- else not <$> checkIndex repository (Quiet `elem` opts)+ else not <$> checkIndex repository (quiet opts) when bad_index $ putInfo opts $ text "Bad index." exitWith $ if failed || bad_index then ExitFailure 1 else ExitSuccess- where- brokenPristine newpris = do- putInfo opts $ text "Looks like we have a difference..."- mc' <- (Just `fmap` readRecorded repository) `catch` (\(_ :: IOException) -> return Nothing)- case mc' of- Nothing -> do- putInfo opts $ text "cannot compute that difference, try repair"- putInfo opts $ text "" $$ text "Inconsistent repository"- Just mc -> do- ftf <- filetypeFunction- Sealed (diff :: FL (PrimOf p) wR wR2)- <- unFreeLeft `fmap` treeDiff (F.diffAlgorithm opts) ftf newpris mc :: IO (Sealed (FL (PrimOf p) wR))- putInfo opts $ case diff of- NilFL -> text "Nothing"- patch -> text "Difference: " <+> showPatch patch- putInfo opts $ text "" $$ text "Inconsistent repository!" --- |check is an alias for repair, with implicit DryRun flag.-check :: DarcsCommand [DarcsFlag]-check = checkAlias { commandCommand = checkCmd- , commandDescription = checkDesc- }- where- checkAlias = commandAlias "check" Nothing repair- checkCmd fps fs = commandCommand repair fps (DryRun : fs)- checkDesc = "Alias for `darcs " ++ commandName repair ++ " --dry-run'."+brokenPristine+ :: forall rt p wR wU wT . (RepoPatch p)+ => [DarcsFlag] -> Repository rt p wR wU wT -> Tree IO -> IO ()+brokenPristine opts repository newpris = do+ putInfo opts $ text "Looks like we have a difference..."+ mc' <- (Just `fmap` readRecorded repository) `catch` (\(_ :: IOException) -> return Nothing)+ case mc' of+ Nothing -> do+ putInfo opts $ text "cannot compute that difference, try repair"+ putInfo opts $ text "" $$ text "Inconsistent repository"+ Just mc -> do+ ftf <- filetypeFunction+ Sealed (diff :: FL (PrimOf p) wR wR2)+ <- unFreeLeft `fmap` treeDiff (diffAlgorithm ? opts) ftf newpris mc :: IO (Sealed (FL (PrimOf p) wR))+ putInfo opts $ case diff of+ NilFL -> text "Nothing"+ patch -> text "Difference: " <+> showNicely patch+ putInfo opts $ text "" $$ text "Inconsistent repository!"
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.UI.Commands.Replace ( replace , defaultToks@@ -27,7 +25,7 @@ import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString as BS+import qualified Data.ByteString as B import Data.Char ( isSpace ) import Data.Maybe ( isJust ) import Control.Exception ( catch, IOException )@@ -36,14 +34,15 @@ , makeBlobBS ) import Darcs.Util.Path( SubPath, toFilePath, AbsolutePath ) import Darcs.UI.Flags- ( DarcsFlag( ForceReplace, Toks )+ ( DarcsFlag , verbosity, useCache, dryRun, umask, diffAlgorithm, fixSubPaths )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Options ( (^), 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.Completion ( knownFileArgs ) import Darcs.Repository.Diff( treeDiff )-import Darcs.Patch ( Patchy, PrimPatch, tokreplace, forceTokReplace+import Darcs.Patch ( PrimPatch, tokreplace, forceTokReplace , maybeApplyToTree ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.RegChars ( regChars )@@ -53,8 +52,6 @@ , addToPending , applyToWorking , readUnrecorded- , readRecordedAndPending- , listRegisteredFiles ) import Darcs.Patch.TokenReplace ( defaultToks ) import Darcs.Repository.Prefs ( FileType(TextFile) )@@ -62,8 +59,6 @@ import Darcs.Patch.Witnesses.Ordered ( FL(..), (+>+), concatFL, toFL, nullFL ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), mapSeal, FreeLeft, Gap(..), unFreeLeft, unseal ) -#include "impossible.h"- replaceDescription :: String replaceDescription = "Substitute one word for another." @@ -123,31 +118,6 @@ "`[[:alnum:]]`) are NOT supported by `--token-chars`, and will be silently\n" ++ "treated as a simple set of characters.\n" -replaceBasicOpts :: DarcsOption a (Maybe String -> Bool -> Maybe String -> a)-replaceBasicOpts = O.tokens ^ O.forceReplace ^ O.workingRepoDir--replaceAdvancedOpts :: DarcsOption a (O.UseIndex -> O.UMask -> a)-replaceAdvancedOpts = O.useIndex ^ O.umask--replaceOpts :: DarcsOption a- (Maybe String- -> Bool- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseIndex- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-replaceOpts = replaceBasicOpts `withStdOpts` replaceAdvancedOpts- replace :: DarcsCommand [DarcsFlag] replace = DarcsCommand { commandProgramName = "darcs"@@ -161,7 +131,7 @@ ] , commandCommand = replaceCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = listRegisteredFiles+ , commandCompleteArgs = replaceArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc replaceAdvancedOpts , commandBasicOptions = odesc replaceBasicOpts@@ -169,27 +139,35 @@ , commandCheckOptions = ocheck replaceOpts , commandParseOptions = onormalise replaceOpts }+ where+ replaceBasicOpts = O.tokens ^ O.forceReplace ^ O.repoDir+ replaceAdvancedOpts = O.useIndex ^ O.umask+ replaceOpts = replaceBasicOpts `withStdOpts` replaceAdvancedOpts +replaceArgs :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO [String]+replaceArgs fps flags args =+ if length args < 2+ then return []+ else knownFileArgs fps flags args+ replaceCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () replaceCmd fps opts (old : new : relfs@(_ : _)) =- withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $ RepoJob $ \repository -> do fs <- fixSubPaths fps relfs- toks <- chooseToks opts old new+ toks <- chooseToks (O.tokens ? opts) old new let checkToken tok = unless (isTok toks tok) $ fail $ "'" ++ tok ++ "' is not a valid token!" mapM_ checkToken [ old, new ] working <- readUnrecorded repository Nothing- pending <- readRecordedAndPending repository files <- filterM (exists working) fs Sealed replacePs <- mapSeal concatFL . toFL <$>- mapM (doReplace toks pending working) files+ mapM (doReplace toks working) files+ -- Note: addToPending takes care of commuting the replace patch and+ -- everything it depends on past the diff between pending and working addToPending repository YesUpdateWorking replacePs- void $ applyToWorking repository (verbosity opts) replacePs `catch` \(e :: IOException) ->- fail $ "Can't do replace on working!\n"- ++ "Perhaps one of the files already" ++ " contains '"- ++ new ++ "'?\n"- ++ show e+ void $ applyToWorking repository (verbosity ? opts) replacePs `catch` \(e :: IOException) ->+ bug $ "Can't do replace on working!\n" ++ show e where exists tree file = if isJust $ findFile tree (floatSubPath file) then return True@@ -199,20 +177,19 @@ skipmsg f = "Skipping file '" ++ toFilePath f ++ "' which isn't in the repository." - doReplace :: forall prim . (Patchy prim, PrimPatch prim,- ApplyState prim ~ Tree) => String -> Tree IO -> Tree IO+ doReplace :: forall prim . (PrimPatch prim,+ ApplyState prim ~ Tree) => String -> Tree IO -> SubPath -> IO (FreeLeft (FL prim))- doReplace toks pend work f = do- let maybeReplace p = isJust <$> maybeApplyToTree replacePatch p- workReplaced <- maybeReplace work- pendReplaced <- maybeReplace pend- if workReplaced && pendReplaced- then return $ joinGap (:>:) (freeGap replacePatch) gapNilFL- else if ForceReplace `elem` opts- then getForceReplace f toks work- else putStrLn existsMsg >> return gapNilFL+ doReplace toks work f = do+ workReplaced <- maybeApplyToTree replacePatch work+ case workReplaced of+ Just _ -> do+ return $ joinGap (:>:) (freeGap replacePatch) gapNilFL+ Nothing+ | O.forceReplace ? opts -> getForceReplace f toks work+ | otherwise -> putStrLn existsMsg >> return gapNilFL where- existsMsg = "Skipping file '" ++ fp ++ "'\nPerhaps the recorded"+ existsMsg = "Skipping file '" ++ fp ++ "'\nPerhaps the working" ++ " version of this file already contains '" ++ new ++ "'?\nUse the --force option to override." gapNilFL = emptyGap NilFL@@ -231,9 +208,9 @@ let path = floatSubPath f content <- readBlob $ fromJust $ findFile tree path let newcontent = forceTokReplace toks (BC.pack new) (BC.pack old)- (BS.concat $ BL.toChunks content)+ (B.concat $ BL.toChunks content) tree' = modifyTree tree path . Just . File $ makeBlobBS newcontent- normaliseNewTokPatch <- treeDiff (diffAlgorithm opts) ftf tree tree'+ normaliseNewTokPatch <- treeDiff (diffAlgorithm ? opts) ftf tree tree' unless (unseal nullFL (unFreeLeft normaliseNewTokPatch)) $ putStrLn $ "Don't be surprised!\n" ++ "I've changed all instances of '" ++ new ++ "' to '"@@ -243,7 +220,7 @@ return . joinGap (+>+) normaliseNewTokPatch $ freeGap $ 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]"+replaceCmd _ _ _ = fail "Usage: darcs replace <OLD> <NEW> <FILE>..." filenameToks :: String filenameToks = "A-Za-z_0-9\\-\\."@@ -262,8 +239,8 @@ -- -- Note: Limitations in the current replace patch file format prevents tokens -- and token-char specifiers from containing any whitespace.-chooseToks :: [DarcsFlag] -> String -> String -> IO String-chooseToks (Toks t : _) a b+chooseToks :: Maybe String -> String -> String -> IO String+chooseToks (Just t) a b | length t <= 2 = badTokenSpec $ "It must contain more than 2 characters, because it" ++ " should be enclosed in square brackets"@@ -284,7 +261,7 @@ spaceyToken x = x ++ " must not contain any space" notAToken x = x ++ " is not a token, according to your spec" -chooseToks (_ : fs) a b = chooseToks fs a b-chooseToks [] a b = if isTok defaultToks a && isTok defaultToks b- then return defaultToks- else return filenameToks+chooseToks Nothing a b =+ if isTok defaultToks a && isTok defaultToks b+ then return defaultToks+ else return filenameToks
@@ -25,14 +25,15 @@ import Data.List ( sort ) import Darcs.UI.Flags- ( DarcsFlag, diffingOpts, verbosity, diffAlgorithm, isInteractive, isUnified+ ( DarcsFlag, diffingOpts, verbosity, diffAlgorithm, isInteractive, withContext , dryRun, umask, useCache, fixSubPaths )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Options ( (^), 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, putInfo ) import Darcs.UI.Commands.Util ( announceFiles ) import Darcs.UI.Commands.Unrevert ( writeUnrevert )+import Darcs.UI.Completion ( modifiedFileArgs ) import Darcs.Util.Global ( debugMessage ) import Darcs.Util.Path ( toFilePath, AbsolutePath ) import Darcs.Repository@@ -42,7 +43,6 @@ , applyToWorking , readRecorded , unrecordedChanges- , listRegisteredFiles ) import Darcs.Patch ( invert, effectOnFilePaths, commute ) import Darcs.Patch.Split ( reversePrimSplitter )@@ -74,45 +74,14 @@ "guaranteed to work if the repository has not changed since `darcs\n" ++ "revert` ran.\n" -revertBasicOpts :: DarcsOption a- (Maybe Bool -> Maybe String -> O.WithContext -> O.DiffAlgorithm -> a)-revertBasicOpts- = O.interactive -- True- ^ O.workingRepoDir- ^ O.withContext- ^ O.diffAlgorithm--revertAdvancedOpts :: DarcsOption a (O.UseIndex -> O.UMask -> a)-revertAdvancedOpts = O.useIndex ^ O.umask--revertOpts :: DarcsOption a- (Maybe Bool- -> Maybe String- -> O.WithContext- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseIndex- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-revertOpts = revertBasicOpts `withStdOpts` revertAdvancedOpts- patchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions patchSelOpts flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = [] , S.interactive = isInteractive True flags , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = O.NoSummary -- option not supported, use default- , S.withContext = isUnified flags+ , S.withContext = withContext ? flags } revert :: DarcsCommand [DarcsFlag]@@ -125,7 +94,7 @@ , commandExtraArgHelp = ["[FILE or DIRECTORY]..."] , commandCommand = revertCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = listRegisteredFiles+ , commandCompleteArgs = modifiedFileArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc revertAdvancedOpts , commandBasicOptions = odesc revertBasicOpts@@ -133,14 +102,23 @@ , commandCheckOptions = ocheck revertOpts , commandParseOptions = onormalise revertOpts }+ where+ revertBasicOpts+ = O.interactive -- True+ ^ O.repoDir+ ^ O.withContext+ ^ O.diffAlgorithm+ revertAdvancedOpts = O.useIndex ^ O.umask+ revertOpts = revertBasicOpts `withStdOpts` revertAdvancedOpts revertCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () revertCmd fps opts args =- withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+ 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 (verbosity opts) files "Reverting changes in"- changes <- unrecordedChanges (diffingOpts opts {- always ScanKnown here -}) repository files+ announceFiles (verbosity ? opts) files "Reverting changes in"+ changes <- unrecordedChanges (diffingOpts opts {- always ScanKnown here -})+ O.NoLookForMoves O.NoLookForReplaces repository files let pre_changed_files = effectOnFilePaths (invert changes) . map toFilePath <$> files recorded <- readRecorded repository Sealed touching_changes <- return (chooseTouching pre_changed_files changes)@@ -149,7 +127,7 @@ _ -> do let context = selectionContextPrim Last "revert" (patchSelOpts opts)- (Just (reversePrimSplitter (diffAlgorithm opts)))+ (Just (reversePrimSplitter (diffAlgorithm ? opts))) pre_changed_files (Just recorded) (norevert:>p) <- runSelection changes context if nullFL p@@ -161,7 +139,7 @@ Just (p':>_) -> writeUnrevert repository p' recorded NilFL Nothing -> writeUnrevert repository (norevert+>+p) recorded NilFL debugMessage "About to apply to the working directory."- _ <- applyToWorking repository (verbosity opts) (invert p) `catch` \(e :: IOException) ->+ _ <- applyToWorking repository (verbosity ? opts) (invert p) `catch` \(e :: IOException) -> fail ("Unable to apply inverse patch!" ++ show e) return () putInfo opts "Finished reverting."
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.UI.Commands.Rollback ( rollback ) where import Prelude ()@@ -34,7 +32,7 @@ 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.Set ( PatchSet(..), patchSet2FL ) import Darcs.Patch.Split ( reversePrimSplitter ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), RL(..), concatFL, nullFL, mapFL_FL )@@ -44,17 +42,18 @@ import Darcs.Repository ( Repository, withRepoLock, RepoJob(..), applyToWorking, readRepo, finalizeRepositoryChanges, tentativelyAddToPending,- considerMergeToWorking, listRegisteredFiles )+ considerMergeToWorking ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, setEnvDarcsPatches,- amInHashedRepository )+ amInHashedRepository, putInfo ) import Darcs.UI.Commands.Unrecord ( getLastPatches ) import Darcs.UI.Commands.Util ( announceFiles )-import Darcs.UI.Flags as F ( DarcsFlag(Quiet), verbosity, umask, useCache,- compression, externalMerge, wantGuiPause,+import Darcs.UI.Completion ( knownFileArgs )+import Darcs.UI.Flags ( DarcsFlag, verbosity, umask, useCache,+ compress, externalMerge, wantGuiPause, diffAlgorithm, fixSubPaths, isInteractive ) import Darcs.UI.Options- ( DarcsOption, (^), odesc, ocheck, onormalise- , defaultFlags, parseFlags+ ( (^), odesc, ocheck, onormalise+ , defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.UI.SelectChanges ( WhichChanges(..),@@ -62,6 +61,7 @@ runSelection ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions(..) ) import Darcs.Util.Path ( toFilePath, AbsolutePath )+import Darcs.Util.Printer ( text ) import Darcs.Util.Progress ( debugMessage ) rollbackDescription :: String@@ -82,40 +82,9 @@ , "and run `unrevert` to restore the saved changes into your working tree." ] -rollbackBasicOpts :: DarcsOption a- ([O.MatchFlag]- -> Maybe Bool -> Maybe String -> O.DiffAlgorithm -> a)-rollbackBasicOpts- = O.matchSeveralOrLast- ^ O.interactive -- True- ^ O.workingRepoDir- ^ O.diffAlgorithm--rollbackAdvancedOpts :: DarcsOption a (O.UMask -> a)-rollbackAdvancedOpts = O.umask--rollbackOpts :: DarcsOption a- ([O.MatchFlag]- -> Maybe Bool- -> Maybe String- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-rollbackOpts = rollbackBasicOpts `withStdOpts` rollbackAdvancedOpts- patchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions patchSelOpts flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = parseFlags O.matchSeveralOrLast flags , S.interactive = isInteractive True flags , S.selectDeps = O.PromptDeps@@ -133,7 +102,7 @@ , commandExtraArgHelp = ["[FILE or DIRECTORY]..."] , commandCommand = rollbackCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = listRegisteredFiles+ , commandCompleteArgs = knownFileArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc rollbackAdvancedOpts , commandBasicOptions = odesc rollbackBasicOpts@@ -141,26 +110,34 @@ , commandCheckOptions = ocheck rollbackOpts , commandParseOptions = onormalise rollbackOpts }+ where+ rollbackBasicOpts+ = O.matchSeveralOrLast+ ^ O.interactive -- True+ ^ O.repoDir+ ^ O.diffAlgorithm+ rollbackAdvancedOpts = O.umask+ rollbackOpts = rollbackBasicOpts `withStdOpts` rollbackAdvancedOpts exitIfNothingSelected :: FL p wX wY -> String -> IO () exitIfNothingSelected ps what = when (nullFL ps) $ putStrLn ("No " ++ what ++ " selected!") >> exitSuccess rollbackCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-rollbackCmd fps opts args = withRepoLock NoDryRun (useCache opts)- YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+rollbackCmd fps opts args = withRepoLock NoDryRun (useCache ? opts)+ YesUpdateWorking (umask ? opts) $ RepoJob $ \repository -> do files <- if null args then return Nothing else Just . sort <$> fixSubPaths fps args when (files == Just []) $ fail "No valid arguments were given."- announceFiles (verbosity opts) files "Rolling back changes in"+ announceFiles (verbosity ? opts) files "Rolling back changes in" allpatches <- readRepo repository let matchFlags = parseFlags O.matchSeveralOrLast opts (_ :> patches) <- return $ if firstMatch matchFlags then getLastPatches matchFlags allpatches- else PatchSet NilRL NilRL :> newset2FL allpatches+ else PatchSet NilRL NilRL :> patchSet2FL allpatches let filesFps = map toFilePath <$> files patchCtx = selectionContext LastReversed "rollback" (patchSelOpts opts) Nothing filesFps (_ :> ps) <-@@ -168,13 +145,13 @@ exitIfNothingSelected ps "patches" setEnvDarcsPatches ps let hunkContext = selectionContextPrim Last "rollback" (patchSelOpts opts)- (Just (reversePrimSplitter (diffAlgorithm opts)))+ (Just (reversePrimSplitter (diffAlgorithm ? opts))) filesFps Nothing- hunks = concatFL . mapFL_FL (canonize $ F.diffAlgorithm opts) . sortCoalesceFL . effect $ ps+ hunks = concatFL . mapFL_FL (canonize $ diffAlgorithm ? opts) . sortCoalesceFL . effect $ ps whatToUndo <- runSelection hunks hunkContext undoItNow opts repository whatToUndo -undoItNow :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree, ApplyState (PrimOf p) ~ Tree)+undoItNow :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => [DarcsFlag] -> Repository rt p wR wU wT -> (q :> FL (PrimOf p)) wA wT -> IO () undoItNow opts repo (_ :> prims) = do@@ -182,19 +159,18 @@ rbp <- n2pia `fmap` anonymous (fromPrims $ invert prims) Sealed pw <- considerMergeToWorking repo "rollback" YesAllowConflictsAndMark YesUpdateWorking- (externalMerge opts) (wantGuiPause opts)- (compression opts) (verbosity opts) NoReorder- (UseIndex, ScanKnown, F.diffAlgorithm opts)+ (externalMerge ? opts) (wantGuiPause opts)+ (compress ? opts) (verbosity ? opts) NoReorder+ (UseIndex, ScanKnown, diffAlgorithm ? opts) NilFL (rbp :>: NilFL) tentativelyAddToPending repo YesUpdateWorking pw finalizeRepositoryChanges repo YesUpdateWorking- (compression opts)- _ <- applyToWorking repo (verbosity opts) pw+ (compress ? opts)+ _ <- applyToWorking repo (verbosity ? opts) pw `catch` \(e :: IOException) -> fail $ "error applying rolled back patch to working directory\n" ++ show e debugMessage "Finished applying unrecorded rollback patch"- when (F.Quiet `notElem` opts) $- putStrLn "Changes rolled back in working directory"+ putInfo opts $ text "Changes rolled back in working directory"
@@ -22,8 +22,6 @@ import Prelude () import Darcs.Prelude -import Prelude hiding ( (^) )- import System.Exit ( exitSuccess #ifndef HAVE_MAPI@@ -44,20 +42,14 @@ ( DarcsCommand(..), withStdOpts , putInfo , putVerbose- , printDryRunMessageAndExit , setEnvDarcsPatches , defaultRepo , amInHashedRepository )+import Darcs.UI.Commands.Util ( printDryRunMessageAndExit, checkUnrelatedRepos ) import Darcs.UI.Flags- ( DarcsFlag( Target- , Context- , Mail- , DryRun- , Quiet- , AllowUnrelatedRepos- )- , willRemoveLogFile, doReverse, dryRun, useCache, remoteRepos, setDefault+ ( DarcsFlag+ , willRemoveLogFile, changesReverse, dryRun, useCache, remoteRepos, setDefault , fixUrl , getCc , getAuthor@@ -65,26 +57,32 @@ , getInReplyTo , getSendmailCmd , getOutput- , getCharset+ , charset , verbosity- , hasSummary , isInteractive- , hasAuthor+ , author , hasLogfile , selectDeps , minimize , editDescription ) import Darcs.UI.Options- ( DarcsOption, (^), odesc, ocheck, onormalise- , defaultFlags, parseFlags+ ( (^), odesc, ocheck, onormalise+ , defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, hopefully, patchDesc )-import Darcs.Repository ( PatchSet, Repository,- identifyRepositoryFor, withRepository, RepoJob(..),- readRepo, readRecorded, prefsUrl, checkUnrelatedRepos )+import Darcs.Repository+ ( Repository+ , repoLocation+ , PatchSet+ , identifyRepositoryFor+ , withRepository+ , RepoJob(..)+ , readRepo+ , readRecorded+ , prefsUrl ) import Darcs.Patch.Set ( Origin ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch ( IsRepoType, RepoPatch, description, applyToTree, invert )@@ -95,6 +93,7 @@ mapFL, mapFL_FL, lengthFL, nullFL ) import Darcs.Patch.Bundle ( minContext, makeBundleN, scanContextFile, patchFilename ) import Darcs.Repository.Prefs ( addRepoSource, getPreflist )+import Darcs.Repository.Flags ( DryRun(..) ) import Darcs.Util.External ( fetchFilePS, Cachable(..) ) import Darcs.UI.External ( signString@@ -127,11 +126,13 @@ import Data.Text.Encoding ( decodeUtf8' ) import Darcs.Util.Progress ( debugMessage ) import Darcs.UI.Email ( makeEmail )+import Darcs.UI.Completion ( prefArgs ) import Darcs.Util.Printer ( Doc, vsep, text, ($$), (<+>), (<>), putDoc, putDocLn- , renderPS, RenderMode(..)+ , renderPS, vcat ) import Darcs.Util.English ( englishNum, Noun(..) )+import Darcs.Util.Text ( sentence, quote ) import Darcs.Util.Path ( FilePathLike, toFilePath, AbsolutePath, AbsolutePathOrStd, getCurrentDirectory, useAbsoluteOrStd, makeAbsoluteOrStd ) import Darcs.Util.Download.HTTP ( postUrl )@@ -139,103 +140,13 @@ import Darcs.Util.Global ( darcsSendMessage, darcsSendMessageFinal ) import Darcs.Util.SignalHandler ( catchInterrupt ) -import qualified Darcs.UI.Message.Send as Msg-#include "impossible.h"--sendBasicOpts :: DarcsOption a- ([O.MatchFlag]- -> O.SelectDeps- -> Maybe Bool- -> O.HeaderFields- -> Maybe String- -> Maybe String- -> (Bool, Maybe String)- -> Maybe O.Output- -> O.Sign- -> O.DryRun- -> O.XmlOutput- -> Maybe O.Summary- -> Bool- -> Maybe Bool- -> Maybe String- -> Bool- -> Bool- -> a)-sendBasicOpts- = O.matchSeveral- ^ O.selectDeps- ^ O.interactive -- True- ^ O.headerFields- ^ O.author- ^ O.charset- ^ O.sendmail- ^ O.output- ^ O.sign- ^ O.dryRunXml- ^ O.summary- ^ O.editDescription- ^ O.setDefault- ^ O.workingRepoDir- ^ O.minimize- ^ O.allowUnrelatedRepos--sendAdvancedOpts :: DarcsOption a- (O.Logfile- -> O.RemoteRepos- -> Maybe AbsolutePath- -> Bool- -> O.NetworkOptions- -> a)-sendAdvancedOpts- = O.logfile- ^ O.remoteRepos- ^ O.sendToContext - ^ O.changesReverse- ^ O.network--sendOpts :: DarcsOption a- ([O.MatchFlag]- -> O.SelectDeps- -> Maybe Bool- -> O.HeaderFields- -> Maybe String- -> Maybe String- -> (Bool, Maybe String)- -> Maybe O.Output- -> O.Sign- -> O.DryRun- -> O.XmlOutput- -> Maybe O.Summary- -> Bool- -> Maybe Bool- -> Maybe String- -> Bool- -> Bool- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.Logfile- -> O.RemoteRepos- -> Maybe AbsolutePath- -> Bool- -> O.NetworkOptions- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-sendOpts = sendBasicOpts `withStdOpts` sendAdvancedOpts- patchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions patchSelOpts flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = parseFlags O.matchSeveral flags , S.interactive = isInteractive True flags- , S.selectDeps = selectDeps flags- , S.summary = hasSummary O.NoSummary flags+ , S.selectDeps = selectDeps ? flags+ , S.summary = O.summary ? flags , S.withContext = O.NoContext } @@ -243,13 +154,13 @@ send = DarcsCommand { commandProgramName = "darcs" , commandName = "send"- , commandHelp = Msg.cmdHelp- , commandDescription = Msg.cmdDescription+ , commandHelp = cmdHelp+ , commandDescription = cmdDescription , commandExtraArgs = 1 , commandExtraArgHelp = ["[REPOSITORY]"] , commandCommand = sendCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = getPreflist "repos"+ , commandCompleteArgs = prefArgs "repos" , commandArgdefaults = defaultRepo , commandAdvancedOptions = odesc sendAdvancedOpts , commandBasicOptions = odesc sendBasicOpts@@ -257,35 +168,59 @@ , commandCheckOptions = ocheck sendOpts , commandParseOptions = onormalise sendOpts }+ where+ sendBasicOpts+ = O.matchSeveral+ ^ O.selectDeps+ ^ O.interactive -- True+ ^ O.headerFields+ ^ O.author+ ^ O.charset+ ^ O.sendmail+ ^ O.output+ ^ O.sign+ ^ O.dryRunXml+ ^ O.summary+ ^ O.editDescription+ ^ O.setDefault+ ^ O.repoDir+ ^ O.minimize+ ^ O.allowUnrelatedRepos+ sendAdvancedOpts+ = O.logfile+ ^ O.remoteRepos+ ^ O.sendToContext + ^ O.changesReverse+ ^ O.network+ sendOpts = sendBasicOpts `withStdOpts` sendAdvancedOpts sendCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-sendCmd fps input_opts [""] = sendCmd fps input_opts []-sendCmd (_,o) input_opts [unfixedrepodir] =- withRepository (useCache input_opts) $ RepoJob $+sendCmd fps opts [""] = sendCmd fps opts []+sendCmd (_,o) opts [unfixedrepodir] =+ withRepository (useCache ? opts) $ RepoJob $ \(repository :: Repository rt p wR wU wR) -> do- context_ps <- the_context input_opts+ context_ps <- the_context (O.sendToContext ? opts) case context_ps of Just them -> do- wtds <- decideOnBehavior input_opts (Nothing :: Maybe (Repository rt p wR wU wR))- sendToThem repository input_opts wtds "CONTEXT" them+ wtds <- decideOnBehavior opts (Nothing :: Maybe (Repository rt p wR wU wR))+ sendToThem repository opts wtds "CONTEXT" them Nothing -> do repodir <- fixUrl o unfixedrepodir -- Test to make sure we aren't trying to push to the current repo here <- getCurrentDirectory when (repodir == toFilePath here) $- fail Msg.cannotSendToSelf+ fail cannotSendToSelf old_default <- getPreflist "defaultrepo"- when (old_default == [repodir] && Quiet `notElem` input_opts) $- putDocLn (Msg.creatingPatch repodir)- repo <- identifyRepositoryFor repository (useCache input_opts) repodir+ when (old_default == [repodir]) $+ putInfo opts (creatingPatch repodir)+ repo <- identifyRepositoryFor repository (useCache ? opts) repodir them <- readRepo repo- addRepoSource repodir (dryRun input_opts) (remoteRepos input_opts) (setDefault False input_opts)- wtds <- decideOnBehavior input_opts (Just repo)- sendToThem repository input_opts wtds repodir them- where the_context [] = return Nothing- the_context (Context foo:_)- = Just `fmap` scanContextFile (toFilePath foo)- the_context (_:fs) = the_context fs+ addRepoSource repodir (dryRun ? opts) (remoteRepos ? opts) (setDefault False opts)+ wtds <- decideOnBehavior opts (Just repo)+ sendToThem repository opts wtds repodir them+ where+ the_context Nothing = return Nothing+ the_context (Just foo) = Just `fmap` scanContextFile (toFilePath foo) sendCmd _ _ _ = impossible sendToThem :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)@@ -293,40 +228,40 @@ -> 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- -- (unless -o/-O or --dry-run is used)- sendmail <- haveSendmail- sm_cmd <- getSendmailCmd opts- when (isNothing (getOutput opts "") && DryRun `notElem` opts &&- not sendmail && sm_cmd == "") $ do- putInfo opts Msg.noWorkingSendmail- exitWith $ ExitFailure 1+ when (fst (O.sendmail ? opts) && dryRun ? opts == O.NoDryRun) $ do+ -- If --mail is used, check if the user has sendmail or+ -- provided a --sendmail-cmd+ sendmail <- haveSendmail+ sm_cmd <- getSendmailCmd opts+ when (not sendmail && sm_cmd == "") $ do+ putInfo opts noWorkingSendmail+ exitWith $ ExitFailure 1 #endif us <- readRepo repo common :> us' <- return $ findCommonWithThem us them- checkUnrelatedRepos (AllowUnrelatedRepos `elem` opts) us them+ checkUnrelatedRepos (O.allowUnrelatedRepos ? opts) us them case us' of- NilFL -> do putInfo opts Msg.nothingSendable+ NilFL -> do putInfo opts nothingSendable exitSuccess- _ -> putVerbose opts $ Msg.selectionIs (mapFL description us')+ _ -> putVerbose opts $ selectionIs (mapFL description us') pristine <- readRecorded repo- let direction = if doReverse opts then FirstReversed else First+ let direction = if changesReverse ? opts then FirstReversed else First context = selectionContext direction "send" (patchSelOpts opts) Nothing Nothing (to_be_sent :> _) <- runSelection us' context printDryRunMessageAndExit "send"- (verbosity opts)- (hasSummary O.NoSummary opts)- (dryRun opts)+ (verbosity ? opts)+ (O.summary ? opts)+ (dryRun ? opts) O.NoXml (isInteractive True opts) to_be_sent when (nullFL to_be_sent) $ do- putInfo opts Msg.selectionIsNull+ putInfo opts selectionIsNull exitSuccess setEnvDarcsPatches to_be_sent let genFullBundle = prepareBundle opts common (Right (pristine, us':\/:to_be_sent))- bundle <- if not (minimize opts)+ bundle <- if not (minimize ? opts) then genFullBundle else do putInfo opts "Minimizing context, to send with full context hit ctrl-C..." ( case minContext common to_be_sent of@@ -338,7 +273,7 @@ fname = make_fname to_be_sent outname = case getOutput opts fname of Just f -> Just f- Nothing | Mail `elem` opts -> Nothing+ Nothing | fst (O.sendmail ? opts) -> Nothing | not $ null [ p | Post p <- wtds] -> Nothing | otherwise -> Just (makeAbsoluteOrStd here fname) case outname of@@ -378,35 +313,35 @@ else take (n-3) st ++ "..." in do thetargets <- getTargets wtds- from <- getAuthor (hasAuthor opts) False+ from <- getAuthor (author ? opts) False let thesubject = fromMaybe (auto_subject to_be_sent) $ getSubject opts (mailcontents, mailfile, mailcharset) <- getDescription opts their_name to_be_sent let warnMailBody = case mailfile of- Just mf -> putDocLn $ Msg.emailBackedUp mf+ Just mf -> putDocLn $ emailBackedUp mf Nothing -> return () warnCharset msg = do- confirmed <- promptYorn $ Msg.promptCharSetWarning msg+ confirmed <- promptYorn $ promptCharSetWarning msg unless confirmed $ do- putDocLn Msg.charsetAborted+ putDocLn charsetAborted warnMailBody exitSuccess - thecharset <- case getCharset opts of+ thecharset <- case charset ? opts of -- Always trust provided charset providedCset@(Just _) -> return providedCset Nothing -> case mailcharset of Nothing -> do- warnCharset Msg.charsetCouldNotGuess+ warnCharset charsetCouldNotGuess return mailcharset Just "utf-8" -> do -- Check the locale encoding for consistency encoding <- getSystemEncoding- debugMessage $ Msg.currentEncodingIs encoding+ debugMessage $ currentEncodingIs encoding unless (isUTF8Locale encoding) $- warnCharset Msg.charsetUtf8MailDiffLocale+ warnCharset charsetUtf8MailDiffLocale return mailcharset -- Trust other cases (us-ascii) Just _ -> return mailcharset@@ -424,7 +359,7 @@ let to = generateEmailToString thetargets sendEmailDoc from to thesubject (getCc opts) sm_cmd contentAndBundle body >>- putInfo opts (Msg.success to (getCc opts))+ putInfo opts (success to (getCc opts)) `catch` \e -> do warnMailBody fail $ ioeGetErrorString e @@ -436,7 +371,7 @@ mmapFilePS fn forM_ [ p | Post p <- thetargets] (\url -> do- putInfo opts $ Msg.postingPatch url+ putInfo opts $ postingPatch url postUrl url (BC.unpack nbody) "message/rfc822") `catch` (\(_ :: IOException) -> sendmail) cleanup opts mailfile@@ -458,18 +393,18 @@ writeBundleToFile opts to_be_sent bundle fname wtds their_name = do (d,f,_) <- getDescription opts their_name to_be_sent let putabs a = do writeDocBinFile a (d $$ bundle)- putDocLn (Msg.wroteBundle a)+ putDocLn (wroteBundle a) putstd = putDoc (d $$ bundle) useAbsoluteOrStd putabs putstd fname let to = generateEmailToString wtds- unless (null to) $ putInfo opts $ Msg.savedButNotSent to+ unless (null to) $ putInfo opts $ savedButNotSent to cleanup opts f data WhatToDo = Post String -- ^ POST the patch via HTTP | SendMail String -- ^ send patch via email -decideOnBehavior :: RepoPatch p => [DarcsFlag] -> Maybe (Repository rt p wR wU wT) -> IO [WhatToDo]+decideOnBehavior :: [DarcsFlag] -> Maybe (Repository rt p wR wU wT) -> IO [WhatToDo] decideOnBehavior opts remote_repo = case the_targets of [] -> do wtds <- case remote_repo of@@ -480,20 +415,17 @@ ts -> do announce_recipients ts return ts where the_targets = collectTargets opts- -- the ifdef above is to so that darcs only checks the remote- -- _darcs/post if we have an implementation of postUrl. See- -- our HTTP module for more details check_post the_remote_repo = do p <- ((readPost . BC.unpack) `fmap`- fetchFilePS (prefsUrl the_remote_repo++"/post")+ fetchFilePS (prefsUrl (repoLocation the_remote_repo) ++ "/post") (MaxAge 600)) `catchall` return [] emails <- who_to_email the_remote_repo return (p++emails) readPost = map parseLine . lines where parseLine t = maybe (Post t) SendMail $ stripPrefix "mailto:" t- who_to_email the_remote_repo =+ who_to_email repo = do email <- (BC.unpack `fmap`- fetchFilePS (prefsUrl the_remote_repo++"/email")+ fetchFilePS (prefsUrl (repoLocation repo) ++ "/email") (MaxAge 600)) `catchall` return "" if '@' `elem` email then return . map SendMail $ lines email@@ -501,18 +433,18 @@ announce_recipients emails = let pn (SendMail s) = s pn (Post p) = p- msg = Msg.willSendTo (dryRun opts) (map pn emails)- in if DryRun `elem` opts- then putInfo opts msg- else when (null the_targets && isNothing (getOutput opts "")) $- putInfo opts msg+ msg = willSendTo (dryRun ? opts) (map pn emails)+ in case dryRun ? opts of+ O.YesDryRun -> putInfo opts msg+ O.NoDryRun -> when (null the_targets && isNothing (getOutput opts "")) $+ putInfo opts msg getTargets :: [WhatToDo] -> IO [WhatToDo]-getTargets [] = fmap ((:[]) . SendMail) $ askUser Msg.promptTarget+getTargets [] = fmap ((:[]) . SendMail) $ askUser promptTarget getTargets wtds = return wtds collectTargets :: [DarcsFlag] -> [WhatToDo]-collectTargets flags = [ f t | Target t <- flags ] where+collectTargets flags = [ f t | t <- O._to (O.headerFields ? flags) ] where f url | "http:" `isPrefixOf` url = Post url f em = SendMail em @@ -521,14 +453,14 @@ getDescription opts their_name patches = case get_filename of Just file -> do- when (editDescription opts) $ do+ when (editDescription ? opts) $ do when (isNothing $ hasLogfile opts) $ writeDocBinFile file patchdesc- debugMessage $ Msg.aboutToEdit file+ debugMessage $ aboutToEdit file (_, changed) <- editFile file unless changed $ do- confirmed <- promptYorn Msg.promptNoDescriptionChange- unless confirmed $ do putDocLn Msg.aborted+ confirmed <- promptYorn promptNoDescriptionChange+ unless confirmed $ do putDocLn aborted exitSuccess return () @@ -549,12 +481,168 @@ return darcsSendMessageFinal) (return . toFilePath) $ hasLogfile opts get_filename = case hasLogfile opts of Just f -> Just $ toFilePath f- Nothing -> if editDescription opts+ Nothing -> if editDescription ? opts then Just darcsSendMessage else Nothing- tryGetCharset content = let body = renderPS Standard content in+ tryGetCharset content = let body = renderPS content in if isAscii body then Just "us-ascii" else either (const Nothing) (const $ Just "utf-8") (decodeUtf8' body)++cmdDescription :: String+cmdDescription =+ "Prepare a bundle of patches to be applied to some target repository."++cmdHelp :: String+cmdHelp = unlines+ [ "Send is used to prepare a bundle of patches that can be applied to a target"+ , "repository. Send accepts the URL of the repository as an argument. When"+ , "called without an argument, send will use the most recent repository that"+ , "was either pushed to, pulled from or sent to. By default, the patch bundle"+ , "is saved to a file, although you may directly send it by mail."+ , ""+ , "The `--output`, `--output-auto-name`, and `--to` flags determine"+ , "what darcs does with the patch bundle after creating it. If you provide an"+ , "`--output` argument, the patch bundle is saved to that file. If you"+ , "specify `--output-auto-name`, the patch bundle is saved to a file with an"+ , "automatically generated name. If you give one or more `--to` arguments,"+ , "the bundle of patches is sent to those locations. The locations may either"+ , "be email addresses or urls that the patch should be submitted to via HTTP."+ , ""+ , "If you provide the `--mail` flag, darcs will look at the contents"+ , "of the `_darcs/prefs/email` file in the target repository (if it exists),"+ , "and send the patch by email to that address. In this case, you may use"+ , "the `--cc` option to specify additional recipients without overriding the"+ , "default repository email address."+ , ""+ , "If `_darcs/prefs/post` exists in the target repository, darcs will"+ , "upload to the URL contained in that file, which may either be a"+ , "`mailto:` URL, or an `http://` URL. In the latter case, the"+ , "patch is posted to that URL."+ , ""+ , "If there is no email address associated with the repository, darcs will"+ , "prompt you for an email address."+ , ""+ , "Use the `--subject` flag to set the subject of the e-mail to be sent."+ , "If you don't provide a subject on the command line, darcs will make one up"+ , "based on names of the patches in the patch bundle."+ , ""+ , "Use the `--in-reply-to` flag to set the In-Reply-To and References headers"+ , "of the e-mail to be sent. By default no additional headers are included so"+ , "e-mail will not be treated as reply by mail readers."+ , ""+ , "If you want to include a description or explanation along with the bundle"+ , "of patches, you need to specify the `--edit-description` flag, which"+ , "will cause darcs to open up an editor with which you can compose a message"+ , "to go along with your patches."+ , ""+ , "If you want to use a command different from the default one for sending"+ , "email, you need to specify a command line with the `--sendmail-command`"+ , "option. The command line can contain some format specifiers which are"+ , "replaced by the actual values. Accepted format specifiers are `%s` for"+ , "subject, `%t` for to, `%c` for cc, `%b` for the body of the mail, `%f` for"+ , "from, `%a` for the patch bundle and the same specifiers in uppercase for the"+ , "URL-encoded values."+ , "Additionally you can add `%<` to the end of the command line if the command"+ , "expects the complete email message on standard input. E.g. the command lines"+ , "for evolution and msmtp look like this:"+ , ""+ , " evolution \"mailto:%T?subject=%S&attach=%A&cc=%C&body=%B\""+ , " msmtp -t %<"+ , ""+ , "Do not confuse the `--author` options with the return address"+ , "that `darcs send` will set for your patch bundle."+ , ""+ , "For example, if you have two email addresses A and B:"+ , ""+ , "* If you use `--author A` but your machine is configured to send mail from"+ , " address B by default, then the return address on your message will be B."+ , "* If you use `--from A` and your mail client supports setting the"+ , " From: address arbitrarily (some non-Unix-like mail clients, especially,"+ , " may not support this), then the return address will be A; if it does"+ , " not support this, then the return address will be B."+ , "* If you supply neither `--from` nor `--author` then the return"+ , " address will be B."+ , ""+ , "In addition, unless you specify the sendmail command with"+ , "`--sendmail-command`, darcs sends email using the default email"+ , "command on your computer. This default command is determined by the"+ , "`configure` script. Thus, on some non-Unix-like OSes,"+ , "`--from` is likely to not work at all."+ ]++cannotSendToSelf :: String+cannotSendToSelf = "Can't send to current repository! Did you mean send --context?"++creatingPatch :: String -> Doc+creatingPatch repodir = "Creating patch to" <+> text (quote repodir) <> "..."++noWorkingSendmail :: Doc+noWorkingSendmail = "No working sendmail instance on your machine!"++nothingSendable :: Doc+nothingSendable = "No recorded local changes to send!"++selectionIs :: [Doc] -> Doc+selectionIs descs = text "We have the following patches to send:" $$ vcat descs++selectionIsNull :: Doc+selectionIsNull = text "You don't want to send any patches, and that's fine with me!"++emailBackedUp :: String -> Doc+emailBackedUp mf = sentence $ "Email body left in" <+> text mf++promptCharSetWarning :: String -> String+promptCharSetWarning msg = "Warning: " ++ msg ++ " Send anyway?"++charsetAborted :: Doc+charsetAborted = "Aborted. You can specify charset with the --charset option."++charsetCouldNotGuess :: String+charsetCouldNotGuess = "darcs could not guess the charset of your mail."++currentEncodingIs :: String -> String+currentEncodingIs e = "Current locale encoding: " ++ e++charsetUtf8MailDiffLocale :: String+charsetUtf8MailDiffLocale = "your mail is valid UTF-8 but your locale differs."++aborted :: Doc+aborted = "Aborted."++success :: String -> String -> Doc+success to cc = sentence $+ "Successfully sent patch bundle to:" <+> text to <+> copies cc+ where+ copies "" = ""+ copies x = "and cc'ed" <+> text x++postingPatch :: String -> Doc+postingPatch url = "Posting patch to" <+> text url++wroteBundle :: FilePathLike a => a -> Doc+wroteBundle a = sentence $ "Wrote patch to" <+> text (toFilePath a)++savedButNotSent :: String -> Doc+savedButNotSent to =+ text ("The usual recipent for this bundle is: " ++ to)+ $$ text "To send it automatically, make sure sendmail is working,"+ <+> text "and add 'send mail' to _darcs/prefs/defaults or"+ <+> text " ~/.darcs/defaults"++willSendTo :: DryRun -> [String] -> Doc+willSendTo dr addresses =+ "Patch bundle" <+> will <+> " be sent to:" <+> text (unwords addresses)+ where+ will = case dr of { YesDryRun -> "would"; NoDryRun -> "will" }++promptTarget :: String+promptTarget = "What is the target email address? "++aboutToEdit :: FilePath -> String+aboutToEdit file = "About to edit file " ++ file++promptNoDescriptionChange :: String+promptNoDescriptionChange = "File content did not change. Continue anyway?"
@@ -15,29 +15,27 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- 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)+import Control.Monad ( when )+import Data.Maybe ( fromMaybe ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository ) import Darcs.UI.Flags ( DarcsFlag, useCache, dryRun, umask)-import Darcs.UI.Options ( DarcsOption, PrimDarcsOption, odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Options+ ( odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Repository.Flags ( UpdateWorking (..) ) import Darcs.Repository ( addToPending, withRepoLock, RepoJob(..) ) import Darcs.Patch ( changepref ) import Darcs.Patch.Witnesses.Ordered ( FL(..) )-import Darcs.Repository.Prefs ( getPrefval, changePrefval, )+import Darcs.Repository.Prefs ( getPrefval, changePrefval ) import Darcs.Util.English ( orClauses ) import Darcs.Util.Path ( AbsolutePath )-#include "impossible.h" -- | A list of all valid preferences for @_darcs/prefs/prefs@. validPrefData :: [(String, String)] -- ^ (name, one line description)@@ -77,28 +75,6 @@ "the repository will always take precedence. This is considered a\n" ++ "low-priority bug, because preferences are seldom set.\n" -setprefBasicOpts :: PrimDarcsOption (Maybe String)-setprefBasicOpts = O.workingRepoDir--setprefAdvancedOpts :: PrimDarcsOption O.UMask-setprefAdvancedOpts = O.umask--setprefOpts :: DarcsOption a- (Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-setprefOpts = setprefBasicOpts `withStdOpts` setprefAdvancedOpts- setpref :: DarcsCommand [DarcsFlag] setpref = DarcsCommand { commandProgramName = "darcs"@@ -109,7 +85,7 @@ , commandExtraArgHelp = ["<PREF>", "<VALUE>"] , commandCommand = setprefCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return validPrefs+ , commandCompleteArgs = completeArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc setprefAdvancedOpts , commandBasicOptions = odesc setprefBasicOpts@@ -117,13 +93,19 @@ , commandCheckOptions = ocheck setprefOpts , commandParseOptions = onormalise setprefOpts }+ where+ setprefBasicOpts = O.repoDir+ setprefAdvancedOpts = O.umask+ setprefOpts = setprefBasicOpts `withStdOpts` setprefAdvancedOpts+ completeArgs _ _ [] = return validPrefs+ completeArgs _ _ _args = return [] setprefCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () setprefCmd _ opts [pref,val] =- withRepoLock (dryRun opts) (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+ withRepoLock (dryRun ? opts) (useCache ? opts) YesUpdateWorking (umask ? opts) $ RepoJob $ \repository -> do when (' ' `elem` pref) $ do putStrLn $ "'"++pref++- "' is not a valid preference name: no spaces allowed!"+ "' is not a valid preference name: no spaces allowed!" exitWith $ ExitFailure 1 when (pref `notElem` validPrefs) $ do putStrLn $ "'"++pref++"' is not a valid preference name!"
@@ -21,11 +21,10 @@ import Darcs.Prelude import Darcs.UI.Commands ( DarcsCommand(..)- , normalCommand, hiddenCommand+ , normalCommand , commandAlias, amInRepository ) import Darcs.UI.Commands.ShowAuthors ( showAuthors )-import Darcs.UI.Commands.ShowBug ( showBug ) import Darcs.UI.Commands.ShowContents ( showContents ) import Darcs.UI.Commands.ShowDependencies ( showDeps ) import Darcs.UI.Commands.ShowFiles ( showFiles )@@ -51,8 +50,7 @@ , commandDescription = showDescription , commandPrereq = amInRepository , commandSubCommands =- [ hiddenCommand showBug- , normalCommand showContents+ [ normalCommand showContents , normalCommand showDeps , normalCommand showFiles , normalCommand showIndex
@@ -22,32 +22,33 @@ import Prelude () import Darcs.Prelude -import Prelude hiding ( (^), catch )+import Prelude hiding ( (^) ) import Control.Arrow ( (&&&), (***) )-import Control.Exception ( catch, IOException ) import Data.Char ( toLower, isSpace ) import Data.Function ( on ) import Data.List ( isInfixOf, sortBy, groupBy, group, sort ) import Data.Maybe( isJust ) import Data.Ord ( comparing )+import System.IO.Error ( catchIOError ) import Text.ParserCombinators.Parsec hiding ( lower, count, Line ) import Text.ParserCombinators.Parsec.Error import Text.Regex ( Regex, mkRegexWithOpts, matchRegex ) -import Darcs.UI.Flags ( DarcsFlag(Verbose), useCache )-import Darcs.UI.Options ( DarcsOption, oid, odesc, ocheck, onormalise, defaultFlags )-import qualified Darcs.UI.Options.All as O ( workingRepoDir, StdCmdAction, Verbosity, UseCache )+import Darcs.UI.Flags ( DarcsFlag, useCache, verbose )+import Darcs.UI.Options ( oid, odesc, ocheck, onormalise, defaultFlags, (?) )+import qualified Darcs.UI.Options.All as O import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, putWarning, amInRepository )+import Darcs.UI.Completion ( noArgs ) import Darcs.UI.External ( viewDoc ) import Darcs.Patch.PatchInfoAnd ( info ) import Darcs.Patch.Info ( piAuthor )-import Darcs.Patch.Set ( newset2RL )+import Darcs.Patch.Set ( patchSet2RL ) import Darcs.Repository ( readRepo, withRepository, RepoJob(..) ) import Darcs.Patch.Witnesses.Ordered ( mapRL )+import Darcs.Util.Lock ( readTextFile ) import Darcs.Util.Printer ( text ) import Darcs.Util.Path ( AbsolutePath )-import qualified Darcs.Util.Ratified as Ratified ( readFile ) data Spelling = Spelling String String [Regex] -- name, email, regexps type ParsedLine = Maybe Spelling -- Nothing for blank lines@@ -95,24 +96,6 @@ " John Snagge <snagge@bbc.co.uk>, John, snagge@, js@(si|mit).edu\n" ++ " Chuck Jones\\, Jr. <chuck@pobox.com>, cj\\+user@example.com\n" -showAuthorsBasicOpts :: DarcsOption a (Maybe String -> a)-showAuthorsBasicOpts = O.workingRepoDir--showAuthorsOpts :: DarcsOption a- (Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-showAuthorsOpts = showAuthorsBasicOpts `withStdOpts` oid- showAuthors :: DarcsCommand [DarcsFlag] showAuthors = DarcsCommand { commandProgramName = "darcs"@@ -123,7 +106,7 @@ , commandExtraArgHelp = [] , commandCommand = authorsCmd , commandPrereq = amInRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = odesc showAuthorsBasicOpts@@ -131,14 +114,17 @@ , commandCheckOptions = ocheck showAuthorsOpts , commandParseOptions = onormalise showAuthorsOpts }+ where+ showAuthorsBasicOpts = O.repoDir+ showAuthorsOpts = showAuthorsBasicOpts `withStdOpts` oid authorsCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-authorsCmd _ flags _ = withRepository (useCache flags) $ RepoJob $ \repository -> do+authorsCmd _ flags _ = withRepository (useCache ? flags) $ RepoJob $ \repository -> do patches <- readRepo repository spellings <- compiledAuthorSpellings flags- let authors = mapRL (piAuthor . info) $ newset2RL patches+ let authors = mapRL (piAuthor . info) $ patchSet2RL patches viewDoc $ text $ unlines $- if Verbose `elem` flags+ if verbose flags then authors else rankAuthors spellings authors @@ -177,9 +163,8 @@ compiledAuthorSpellings :: [DarcsFlag] -> IO [Spelling] compiledAuthorSpellings flags = do let as_file = ".authorspellings"- contents <- Ratified.readFile -- never unlinked from within darcs- as_file `catch` (\(_ :: IOException) -> return "")- let parse_results = map (parse sentence as_file) $ lines contents+ content_lines <- readTextFile as_file `catchIOError` (const (return []))+ let parse_results = map (parse sentence as_file) content_lines clean 1 parse_results where clean :: Int -> [Either ParseError ParsedLine] -> IO [Spelling]
@@ -1,80 +0,0 @@--- Copyright (C) 2007 Eric Kow------ 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.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 )-import qualified Darcs.UI.Options.All as O ( workingRepoDir, StdCmdAction, Verbosity, UseCache )-import Darcs.Util.Path ( AbsolutePath )-#include "impossible.h"--showBugDescription :: String-showBugDescription = "Simulate a run-time failure."--showBugHelp :: String-showBugHelp =- "Show bug can be used to see what darcs would show you if you encountered.\n"- ++"a bug in darcs.\n"--showBugBasicOpts :: DarcsOption a (Maybe String -> a)-showBugBasicOpts = O.workingRepoDir--showBugOpts :: DarcsOption a- (Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)--showBugOpts = showBugBasicOpts `withStdOpts` oid--showBug :: DarcsCommand [DarcsFlag]-showBug = DarcsCommand- { commandProgramName = "darcs"- , commandName = "bug"- , commandHelp = showBugHelp- , commandDescription = showBugDescription- , commandExtraArgs = 0- , commandExtraArgHelp = []- , commandCommand = showBugCmd- , commandPrereq = findRepository- , commandGetArgPossibilities = return []- , commandArgdefaults = nodefaults- , commandAdvancedOptions = []- , commandBasicOptions = odesc showBugBasicOpts- , commandDefaults = defaultFlags showBugOpts- , commandCheckOptions = ocheck showBugOpts- , commandParseOptions = onormalise showBugOpts- }--showBugCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-showBugCmd _ _ _ = bug "This is actually a fake bug in darcs."-
@@ -29,15 +29,10 @@ import qualified Data.ByteString.Lazy as BL import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, findRepository )+import Darcs.UI.Completion ( noArgs ) import Darcs.UI.Flags ( DarcsFlag, useCache, fixSubPaths )-import Darcs.UI.Options ( DarcsOption, (^), oid, odesc, ocheck, onormalise, defaultFlags, parseFlags )+import Darcs.UI.Options ( (^), oid, odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O- ( MatchFlag- , matchUpToOne- , workingRepoDir- , StdCmdAction- , Verbosity- , UseCache ) import Darcs.Patch.Match ( haveNonrangeMatch ) import Darcs.Repository ( withRepository, RepoJob(..), readRecorded, repoPatchType ) import Darcs.Util.Lock ( withDelayedDir )@@ -55,25 +50,6 @@ "If you give show contents no version arguments, it displays the recorded\n"++ "version of the file(s).\n" -showContentsBasicOpts :: DarcsOption a ([O.MatchFlag] -> Maybe String -> a)-showContentsBasicOpts = O.matchUpToOne ^ O.workingRepoDir--showContentsOpts :: DarcsOption a- ([O.MatchFlag]- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-showContentsOpts = O.matchUpToOne ^ O.workingRepoDir `withStdOpts` oid- showContents :: DarcsCommand [DarcsFlag] showContents = DarcsCommand { commandProgramName = "darcs"@@ -84,7 +60,7 @@ , commandExtraArgHelp = ["[FILE]..."] , commandCommand = showContentsCmd , commandPrereq = findRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = odesc showContentsBasicOpts@@ -92,13 +68,16 @@ , commandCheckOptions = ocheck showContentsOpts , commandParseOptions = onormalise showContentsOpts }+ where+ showContentsBasicOpts = O.matchUpToOne ^ O.repoDir+ showContentsOpts = O.matchUpToOne ^ O.repoDir `withStdOpts` oid showContentsCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () 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.matchUpToOne opts- withRepository (useCache opts) $ RepoJob $ \repository -> do+ withRepository (useCache ? opts) $ RepoJob $ \repository -> do let readContents = do okpaths <- filterM TM.fileExists floatedPaths forM okpaths $ \f -> (B.concat . BL.toChunks) `fmap` TM.readFile f
@@ -12,35 +12,28 @@ 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.Repository ( readRepo, withRepositoryLocation, RepoJob(..) ) import Darcs.UI.Flags ( DarcsFlag(..), getRepourl- , useCache, toMatchFlags )-import Darcs.UI.Options ( PrimDarcsOption, oid, odesc, ocheck, onormalise, defaultFlags )+ , useCache )+import Darcs.UI.Options ( 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.UI.Completion ( noArgs ) 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.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.Choices ( unLabel, 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(..) ) +import Darcs.Patch.Witnesses.Ordered ( (:>)(..), (:>)(..), foldlFL, mapFL_FL ) showDepsDescription :: String showDepsDescription = "Generate the graph of dependencies."@@ -57,25 +50,6 @@ , "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"@@ -86,7 +60,7 @@ , commandExtraArgHelp = [] , commandCommand = depsCmd , commandPrereq = findRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = odesc showDepsBasicOpts@@ -94,16 +68,18 @@ , commandCheckOptions = ocheck showDepsOpts , commandParseOptions = onormalise showDepsOpts }+ where+ showDepsBasicOpts = O.matchSeveralOrLast+ showDepsOpts = showDepsBasicOpts `withStdOpts` oid 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+ withRepositoryLocation (useCache ? opts) repodir $ RepoJob $ \repo -> do+ Sealed2 rFl <- readRepo repo >>= pruneRepo+ let deps = getDeps (removeInternalFL . mapFL_FL hopefully $ rFl) rFl dGraph = transitiveReduction $@@ -119,29 +95,29 @@ , ImageScale UniformScale ] }- pruneRepo r = let matchFlags = toMatchFlags opts in+ pruneRepo r = let matchFlags = O.matchSeveralOrLast ? opts in if firstMatch matchFlags then case getLastPatches matchFlags r of- Sealed2 ps -> return $ seal2 $ PatchSet NilRL ps+ Sealed2 ps -> return $ seal2 ps else case matchingHead matchFlags r of- _ :> patches -> return $ seal2 $ PatchSet NilRL $ reverseFL patches+ _ :> patches -> return $ seal2 patches getLastPatches matchFlags ps = case matchFirstPatchset matchFlags ps of Sealed p1s -> case findCommonWithThem ps p1s of- _ :> ps' -> seal2 $ reverseFL ps'+ _ :> ps' -> seal2 ps' -makeGraph :: (RepoPatch p,ApplyState p ~ Tree) => [SPatchAndDeps p] -> DepsGraph+makeGraph :: [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+ mkNode = 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+ mkLabel = formatText 20 . (:[]) . piName . patch2patchinfo . unLabel mkLNode :: LabelledPatch (Named p) wX wY -> LNode String mkLNode p = (mkNode p, mkLabel p) mkUEdges :: [UEdge]
@@ -15,18 +15,17 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-} 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- , defaultFlags, parseFlags )+import Darcs.UI.Options ( (^), oid, odesc, ocheck, onormalise+ , defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInRepository )+import Darcs.UI.Completion ( knownFileArgs ) import Darcs.Repository ( Repository, withRepository, RepoJob(..), repoPatchType ) import Darcs.Patch ( IsRepoType, RepoPatch )@@ -39,7 +38,7 @@ import Data.List( isPrefixOf ) -import Darcs.Patch.Match ( haveNonrangeMatch )+import Darcs.Patch.Match ( haveNonrangeExplicitMatch ) import Darcs.Repository.Match ( getNonrangeMatch ) import Darcs.Util.Lock ( withDelayedDir ) @@ -70,53 +69,33 @@ "\n" ++ " darcs show files -0 | xargs -0 ls -ldS\n" -showFilesBasicOpts :: DarcsOption a- (Bool -> Bool -> Bool -> Bool -> [O.MatchFlag] -> Maybe String -> a)-showFilesBasicOpts- = O.files- ^ O.directories- ^ O.pending- ^ O.nullFlag- ^ O.matchUpToOne- ^ O.workingRepoDir--showFilesOpts :: DarcsOption a- (Bool- -> Bool- -> Bool- -> Bool- -> [O.MatchFlag]- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-showFilesOpts = showFilesBasicOpts `withStdOpts` oid- showFiles :: DarcsCommand [DarcsFlag]-showFiles = DarcsCommand {- commandProgramName = "darcs",- commandName = "files",- commandHelp = showFilesHelp,- commandDescription = showFilesDescription,- commandExtraArgs = -1,- commandExtraArgHelp = ["[FILE or DIRECTORY]..."],- commandCommand = manifestCmd toListFiles,- commandPrereq = amInRepository,- commandGetArgPossibilities = return [],- commandArgdefaults = nodefaults,- commandAdvancedOptions = [],- commandBasicOptions = odesc showFilesBasicOpts,- commandDefaults = defaultFlags showFilesOpts,- commandCheckOptions = ocheck showFilesOpts,- commandParseOptions = onormalise showFilesOpts }+showFiles = DarcsCommand+ { commandProgramName = "darcs"+ , commandName = "files"+ , commandHelp = showFilesHelp+ , commandDescription = showFilesDescription+ , commandExtraArgs = -1+ , commandExtraArgHelp = ["[FILE or DIRECTORY]..."]+ , commandCommand = manifestCmd toListFiles+ , commandPrereq = amInRepository+ , commandCompleteArgs = knownFileArgs+ , commandArgdefaults = nodefaults+ , commandAdvancedOptions = []+ , commandBasicOptions = odesc showFilesBasicOpts+ , commandDefaults = defaultFlags showFilesOpts+ , commandCheckOptions = ocheck showFilesOpts+ , commandParseOptions = onormalise showFilesOpts+ }+ where+ showFilesBasicOpts+ = O.files+ ^ O.directories+ ^ O.pending+ ^ O.nullFlag+ ^ O.matchUpToOne+ ^ O.repoDir+ showFilesOpts = showFilesBasicOpts `withStdOpts` oid toListFiles :: [DarcsFlag] -> Tree m -> [FilePath] toListFiles opts = filesDirs (parseFlags O.files opts) (parseFlags O.directories opts)@@ -137,7 +116,7 @@ manifestHelper :: ([DarcsFlag] -> Tree IO -> [FilePath]) -> [DarcsFlag] -> [String] -> IO [FilePath] manifestHelper to_list opts argList = do- list' <- to_list opts `fmap` withRepository (useCache opts) (RepoJob slurp)+ list' <- to_list opts `fmap` withRepository (useCache ? opts) (RepoJob slurp) case argList of [] -> return list' prefixes -> return (onlysubdirs prefixes list')@@ -146,7 +125,7 @@ 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+ let fUpto = haveNonrangeExplicitMatch (repoPatchType r) matchFlags fPending = parseFlags O.pending opts -- this covers all 4 possibilities case (fUpto,fPending) of
@@ -20,7 +20,6 @@ -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -- SOFTWARE. -{-# LANGUAGE CPP #-} module Darcs.UI.Commands.ShowIndex ( showIndex , showPristineCmd -- for alias@@ -30,9 +29,10 @@ import Darcs.Prelude import Control.Monad ( (>=>) )-import Darcs.UI.Flags ( DarcsFlag(NullFlag), useCache )+import Darcs.UI.Flags ( DarcsFlag, useCache ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInRepository )-import Darcs.UI.Options ( DarcsOption, (^), oid, odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Completion ( noArgs )+import Darcs.UI.Options ( (^), oid, odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Repository ( withRepository, RepoJob(..), readIndex ) import Darcs.Repository.State ( readRecorded )@@ -44,62 +44,44 @@ import System.Posix.Types ( FileID ) -import qualified Data.ByteString.Char8 as BS+import qualified Data.ByteString.Char8 as BC import Data.Maybe ( fromJust ) import qualified Data.Map as M ( Map, lookup, fromList ) -showIndexBasicOpts :: DarcsOption a- (Bool -> Bool -> Bool -> Maybe String -> a)-showIndexBasicOpts = O.files ^ O.directories ^ O.nullFlag ^ O.workingRepoDir--showIndexOpts :: DarcsOption a- (Bool- -> Bool- -> Bool- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-showIndexOpts = showIndexBasicOpts `withStdOpts` oid- showIndex :: DarcsCommand [DarcsFlag]-showIndex = DarcsCommand {- commandProgramName = "darcs",- commandName = "index",- commandDescription = "Dump contents of working tree index.",- commandHelp =- "The `darcs show index` command lists all version-controlled files and " ++- "directories along with their hashes as stored in `_darcs/index`. " ++- "For files, the fields correspond to file size, sha256 of the current " ++- "file content and the filename.",- commandExtraArgs = 0,- commandExtraArgHelp = [],- commandCommand = showIndexCmd,- commandPrereq = amInRepository,- commandGetArgPossibilities = return [],- commandArgdefaults = nodefaults,- commandAdvancedOptions = [],- commandBasicOptions = odesc showIndexBasicOpts,- commandDefaults = defaultFlags showIndexOpts,- commandCheckOptions = ocheck showIndexOpts,- commandParseOptions = onormalise showIndexOpts }+showIndex = DarcsCommand+ { commandProgramName = "darcs"+ , commandName = "index"+ , commandDescription = "Dump contents of working tree index."+ , commandHelp =+ "The `darcs show index` command lists all version-controlled files and " +++ "directories along with their hashes as stored in `_darcs/index`. " +++ "For files, the fields correspond to file size, sha256 of the current " +++ "file content and the filename."+ , commandExtraArgs = 0+ , commandExtraArgHelp = []+ , commandCommand = showIndexCmd+ , commandPrereq = amInRepository+ , commandCompleteArgs = noArgs+ , commandArgdefaults = nodefaults+ , commandAdvancedOptions = []+ , commandBasicOptions = odesc showIndexBasicOpts+ , commandDefaults = defaultFlags showIndexOpts+ , commandCheckOptions = ocheck showIndexOpts+ , commandParseOptions = onormalise showIndexOpts+ }+ where+ showIndexBasicOpts = O.nullFlag ^ O.repoDir+ showIndexOpts = showIndexBasicOpts `withStdOpts` oid dump :: [DarcsFlag] -> Maybe (M.Map FilePath FileID) -> Tree IO -> IO () dump opts fileids tree = do- let line | NullFlag `elem` opts = \t -> putStr t >> putChar '\0'+ let line | O.nullFlag ? opts = \t -> putStr t >> putChar '\0' | otherwise = putStrLn output (p, i) = do let hash = case itemHash i of NoHash -> "(no hash available)"- h -> BS.unpack $ encodeBase16 h+ h -> BC.unpack $ encodeBase16 h path = anchorPath "" p isdir = case i of SubTree _ -> "/"@@ -112,13 +94,13 @@ mapM_ output $ (floatPath ".", SubTree x) : list x showIndexCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-showIndexCmd _ opts _ = withRepository (useCache opts) $ RepoJob $ \repo ->+showIndexCmd _ opts _ = withRepository (useCache ? opts) $ RepoJob $ \repo -> do index <- readIndex repo index_tree <- updateIndex index fileids <- (M.fromList . map (\((a,_),b) -> (anchorPath "" a,b))) <$> listFileIDs index dump opts (Just fileids) index_tree showPristineCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-showPristineCmd _ opts _ = withRepository (useCache opts) $ RepoJob $+showPristineCmd _ opts _ = withRepository (useCache ? opts) $ RepoJob $ readRecorded >=> dump opts Nothing
@@ -3,67 +3,51 @@ import Prelude () import Darcs.Prelude -import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )-import Darcs.UI.Flags ( DarcsFlag(Verbose), useCache )+import Darcs.UI.Commands+ ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )+import Darcs.UI.Completion ( noArgs )+import Darcs.UI.Flags ( DarcsFlag, useCache, verbose ) import Prelude hiding ( (^) )-import Darcs.UI.Options ( DarcsOption, (^), oid, odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Options+ ( (^), oid, odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Util.Path ( AbsolutePath )-import Darcs.Repository.InternalTypes ( Repository(..) )-import Darcs.Repository ( withRepository, RepoJob(..) )+import Darcs.Repository ( withRepository, RepoJob(..), repoLocation ) import Darcs.Repository.PatchIndex-import Control.Arrow ()--showPatchIndexBasicOpts :: DarcsOption a- (Bool -> Bool -> Bool -> Maybe String -> a)-showPatchIndexBasicOpts = O.files ^ O.directories ^ O.nullFlag ^ O.workingRepoDir--showPatchIndexOpts :: DarcsOption a- (Bool- -> Bool- -> Bool- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-showPatchIndexOpts = showPatchIndexBasicOpts `withStdOpts` oid+ ( dumpPatchIndex, piTest, doesPatchIndexExist, isPatchIndexInSync) showPatchIndex :: DarcsCommand [DarcsFlag]-showPatchIndex = DarcsCommand {- commandProgramName = "darcs",- commandName = "patch-index",- commandDescription = "Check integrity of patch index",- commandHelp =- "When given the `--verbose` flag, the command dumps the complete content\n"- ++ "of the patch index and checks its integrity.",- commandExtraArgs = 0,- commandExtraArgHelp = [],- commandCommand = showPatchIndexCmd,- commandPrereq = amInHashedRepository,- commandGetArgPossibilities = return [],- commandArgdefaults = nodefaults,- commandAdvancedOptions = [],- commandBasicOptions = odesc showPatchIndexBasicOpts,- commandDefaults = defaultFlags showPatchIndexOpts,- commandCheckOptions = ocheck showPatchIndexOpts,- commandParseOptions = onormalise showPatchIndexOpts }+showPatchIndex = DarcsCommand+ { commandProgramName = "darcs"+ , commandName = "patch-index"+ , commandDescription = "Check integrity of patch index"+ , commandHelp =+ "When given the `--verbose` flag, the command dumps the complete content\n" +++ "of the patch index and checks its integrity."+ , commandExtraArgs = 0+ , commandExtraArgHelp = []+ , commandCommand = showPatchIndexCmd+ , commandPrereq = amInHashedRepository+ , commandCompleteArgs = noArgs+ , commandArgdefaults = nodefaults+ , commandAdvancedOptions = []+ , commandBasicOptions = odesc showPatchIndexBasicOpts+ , commandDefaults = defaultFlags showPatchIndexOpts+ , commandCheckOptions = ocheck showPatchIndexOpts+ , commandParseOptions = onormalise showPatchIndexOpts+ }+ where+ showPatchIndexBasicOpts = O.nullFlag ^ O.repoDir+ showPatchIndexOpts = showPatchIndexBasicOpts `withStdOpts` oid showPatchIndexCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () showPatchIndexCmd _ opts _- | Verbose `elem` opts = do- withRepository (useCache opts) $ RepoJob $ \repo@(Repo repodir _ _ _) ->- dumpPatchIndex repodir >> piTest repo+ | verbose opts =+ withRepository (useCache ? opts) $ RepoJob $ \repo ->+ let loc = repoLocation repo in dumpPatchIndex loc >> piTest loc | otherwise =- withRepository (useCache opts) $ RepoJob $ \(repo@(Repo repodir _ _ _)) -> do- ex <- doesPatchIndexExist repodir+ withRepository (useCache ? opts) $ RepoJob $ \repo -> do+ ex <- doesPatchIndexExist (repoLocation repo) if ex then do sy <- isPatchIndexInSync repo if sy
@@ -15,31 +15,35 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# 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 ) import Control.Monad ( when, unless, liftM ) import Text.Html ( tag, stringToHtml ) import Darcs.Util.Path ( AbsolutePath )-import Darcs.UI.Flags ( DarcsFlag(XMLOutput, Verbose, NoFiles), useCache )-import Darcs.UI.Options ( DarcsOption, (^), oid, odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Flags ( DarcsFlag, useCache, hasXmlOutput, verbose, enumeratePatches )+import Darcs.UI.Options ( (^), oid, odesc, ocheck, onormalise, defaultFlags, (?) ) 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(..), 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.UI.Completion ( noArgs )+import Darcs.Repository+ ( Repository+ , repoFormat+ , repoLocation+ , repoPristineType+ , repoCache+ , withRepository+ , RepoJob(..)+ , readRepo )+import Darcs.Repository.Hashed( repoXor )+import Darcs.Repository.PatchIndex ( isPatchIndexDisabled, doesPatchIndexExist )+import Darcs.Repository.Prefs ( getPreflist, getMotd ) import Darcs.Patch ( IsRepoType, RepoPatch )-import Darcs.Patch.Set ( newset2RL )+import Darcs.Patch.Set ( patchSet2RL ) import Darcs.Patch.Witnesses.Ordered ( lengthRL ) import qualified Data.ByteString.Char8 as BC (unpack) import Darcs.Patch.Apply( ApplyState )@@ -52,39 +56,21 @@ "without inspecting `_darcs` directly (and without breaking when the\n" ++ "`_darcs` format changes).\n" ++ "\n" ++- "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 includes statistics that require walking through the patches\n" +++ "recorded in the repository, namely the 'Weak Hash' and the count of patches.\n" +++ "If this data isn't needed, use `--no-enum-patches` to accelerate this command\n" +++ "from O(n) to O(1).\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" showRepoDescription :: String showRepoDescription = "Show repository summary information" -showRepoBasicOpts :: DarcsOption a (Maybe String -> Bool -> O.XmlOutput -> a)-showRepoBasicOpts = O.workingRepoDir ^ O.files ^ O.xmloutput--showRepoOpts :: DarcsOption a- (Maybe String- -> Bool- -> O.XmlOutput- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-showRepoOpts = showRepoBasicOpts `withStdOpts` oid- showRepo :: DarcsCommand [DarcsFlag] showRepo = DarcsCommand { commandProgramName = "darcs"@@ -95,7 +81,7 @@ , commandExtraArgHelp = [] , commandCommand = repoCmd , commandPrereq = amInRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = odesc showRepoBasicOpts@@ -103,10 +89,16 @@ , commandCheckOptions = ocheck showRepoOpts , commandParseOptions = onormalise showRepoOpts }+ where+ showRepoBasicOpts = O.repoDir ^ O.xmlOutput ^ O.enumPatches+ showRepoOpts = showRepoBasicOpts `withStdOpts` oid repoCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO ()-repoCmd _ opts _ = let put_mode = if XMLOutput `elem` opts then showInfoXML else showInfoUsr- in withRepository (useCache opts) $ RepoJob $ \repository -> actuallyShowRepo (putInfo put_mode) repository opts+repoCmd _ opts _ =+ let put_mode = if hasXmlOutput opts then showInfoXML else showInfoUsr+ in withRepository (useCache ? opts) $+ RepoJob $ \repository ->+ actuallyShowRepo (putInfo put_mode) repository opts -- Some convenience functions to output a labelled text string or an -- XML tag + value (same API). If no value, output is suppressed@@ -123,11 +115,11 @@ safeTag ('#':cs) = "num_" ++ safeTag cs safeTag (c:cs) = toLower c : safeTag cs --- labelled strings: labels are right-aligned at 14 characters;+-- labelled strings: labels are right-aligned at 15 characters; -- subsequent lines in multi-line output are indented accordingly. showInfoUsr :: ShowInfo-showInfoUsr t i = replicate (14 - length t) ' ' ++ t ++ ": " ++- intercalate ('\n' : replicate 16 ' ') (lines i) ++ "\n"+showInfoUsr t i = replicate (15 - length t) ' ' ++ t ++ ": " +++ intercalate ('\n' : replicate 17 ' ') (lines i) ++ "\n" type PutInfo = String -> String -> IO () putInfo :: ShowInfo -> PutInfo@@ -137,19 +129,29 @@ -- 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 :: (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)- showRepoFormat out rf- out "Root" loc- showRepoAux out pris cs- showRepoPrefs out- unless (NoFiles `elem` opts) (numPatches r >>= (out "Num Patches" . show ))- unless (NoFiles `elem` opts) (showXor out r)- showRepoMOTD out r- when (XMLOutput `elem` opts) (putStr "</repository>\n")+actuallyShowRepo+ :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+ => PutInfo -> Repository rt p wR wU wR -> [DarcsFlag] -> IO ()+actuallyShowRepo out r opts = do+ when (hasXmlOutput opts) (putStr "<repository>\n")+ when (verbose opts) (out "Show" $ show r)+ out "Format" $ showInOneLine $ repoFormat r+ let loc = repoLocation r+ out "Root" loc+ out "PristineType" $ show $ repoPristineType r+ out "Cache" $ showInOneLine $ repoCache r+ piExists <- doesPatchIndexExist loc+ piDisabled <- isPatchIndexDisabled loc+ out "PatchIndex" $+ case (piExists, piDisabled) of+ (_, True) -> "disabled"+ (True, False) -> "enabled"+ (False, False) -> "enabled, but not yet created"+ showRepoPrefs out+ when (enumeratePatches opts) (do numPatches r >>= (out "Num Patches" . show)+ showXor out r)+ showRepoMOTD out r+ when (hasXmlOutput opts) (putStr "</repository>\n") showXor :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => PutInfo -> Repository rt p wR wU wR -> IO ()@@ -162,14 +164,8 @@ -- the routines below work to provide more human-readable information -- regarding the repository elements. -showRepoFormat :: PutInfo -> RepoFormat -> IO ()-showRepoFormat out rf = out "Format" . intercalate ", " . lines . show $ rf--showRepoAux :: PutInfo -> Pristine -> Cache -> IO ()-showRepoAux out pris cs =- do out "Pristine" $ show pris- out "Cache" $ intercalate ", " $ lines $ show cs-+showInOneLine :: Show a => a -> String+showInOneLine = intercalate ", " . lines . show showRepoPrefs :: PutInfo -> IO () showRepoPrefs out = do@@ -179,10 +175,9 @@ where prefOut = uncurry out . (\(p,v) -> (p++" Pref", dropWhile isSpace v)) . break isSpace showRepoMOTD :: RepoPatch p => PutInfo -> Repository rt p wR wU wR -> IO ()-showRepoMOTD out (Repo loc _ _ _) = getMotd loc >>= out "MOTD" . BC.unpack+showRepoMOTD out repo = getMotd (repoLocation repo) >>= out "MOTD" . BC.unpack -- Support routines to provide information used by the PutInfo operations above. numPatches :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wR -> IO Int-numPatches r = (lengthRL . newset2RL) `liftM` readRepo r-+numPatches r = (lengthRL . patchSet2RL) `liftM` readRepo r
@@ -27,14 +27,12 @@ import System.IO ( stderr, hPutStrLn ) import Darcs.Patch.Set ( PatchSet(..) )-import Darcs.Repository ( readRepo, withRepositoryDirectory, RepoJob(..) )+import Darcs.Repository ( readRepo, withRepositoryLocation, RepoJob(..) ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, findRepository )-import Darcs.UI.Commands.Tag ( getTags )+import Darcs.UI.Commands.Util ( repoTags )+import Darcs.UI.Completion ( noArgs ) import Darcs.UI.Flags ( DarcsFlag, useCache, getRepourl )-import Darcs.UI.Options- ( DarcsOption, PrimDarcsOption- , oid, odesc, ocheck, onormalise, defaultFlags- )+import Darcs.UI.Options ( oid, odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Util.Text ( formatText ) import Darcs.Util.Path ( AbsolutePath )@@ -51,24 +49,6 @@ ++ "if this happens." ] -showTagsBasicOpts :: PrimDarcsOption (Maybe String)-showTagsBasicOpts = O.possiblyRemoteRepo--showTagsOpts :: DarcsOption a- (Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-showTagsOpts = showTagsBasicOpts `withStdOpts` oid- showTags :: DarcsCommand [DarcsFlag] showTags = DarcsCommand { commandProgramName = "darcs"@@ -79,7 +59,7 @@ , commandExtraArgHelp = [] , commandCommand = tagsCmd , commandPrereq = findRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = [] , commandBasicOptions = odesc showTagsBasicOpts@@ -87,14 +67,17 @@ , commandCheckOptions = ocheck showTagsOpts , commandParseOptions = onormalise showTagsOpts }+ where+ showTagsBasicOpts = O.possiblyRemoteRepo+ showTagsOpts = showTagsBasicOpts `withStdOpts` oid tagsCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () tagsCmd _ opts _ = let repodir = fromMaybe "." (getRepourl opts) in- withRepositoryDirectory (useCache opts) repodir $ RepoJob $ \repo ->+ withRepositoryLocation (useCache ? opts) repodir $ RepoJob $ \repo -> readRepo repo >>= printTags where printTags :: PatchSet rt p wW wZ -> IO ()- printTags = join . fmap (sequence_ . map process) . getTags+ printTags = join . fmap (sequence_ . map process) . repoTags process :: String -> IO () process t = normalize t t False >>= putStrLn normalize :: String -> String -> Bool -> IO String
@@ -15,37 +15,45 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -module Darcs.UI.Commands.Tag ( tag, getTags ) where+module Darcs.UI.Commands.Tag ( tag ) where import Prelude () import Darcs.Prelude import Control.Monad ( when )-import Data.Maybe ( catMaybes )+import System.IO ( hPutStr, stderr ) -import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )-import Darcs.UI.Flags- ( DarcsFlag(AskDeps), getDate, compression, verbosity, useCache, umask, getAuthor- , 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.Apply ( ApplyState )+import Darcs.Patch.Info ( patchinfo )+import Darcs.Patch.Depends ( getUncovered ) import Darcs.Patch- ( Patchy, PrimPatch, PrimOf+ ( 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.Named.Wrapped ( infopatch, adddeps, runInternalChecker, namedInternalChecker )-import Darcs.Patch.Set ( PatchSet(..), emptyPatchSet, appendPSFL, newset2FL, patchSetfMap )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, n2pia, hopefully )+import Darcs.Patch.Named.Wrapped+ ( infopatch, adddeps, runInternalChecker, namedInternalChecker )+import Darcs.Patch.Set+ ( PatchSet(..), emptyPatchSet, appendPSFL, patchSet2FL ) import Darcs.Patch.Witnesses.Ordered ( FL(..), filterOutRLRL, (:>)(..) ) import Darcs.Patch.Witnesses.Sealed ( Sealed(..), mapSeal )++import Darcs.Repository+ ( withRepoLock, Repository, RepoJob(..), readRepo+ , tentativelyAddPatch, finalizeRepositoryChanges,+ )+import Darcs.Repository.Flags ( UpdateWorking(..), DryRun(NoDryRun) )++import Darcs.UI.Commands+ ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )+import Darcs.UI.Commands.Util ( repoTags )+import Darcs.UI.Completion ( noArgs )+import Darcs.UI.Flags+ ( DarcsFlag, getDate, compress, verbosity, useCache, umask, getAuthor, author )+import Darcs.UI.Options+ ( (^), odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) )+import qualified Darcs.UI.Options.All as O+import Darcs.UI.PatchHeader ( getLog ) import Darcs.UI.SelectChanges ( WhichChanges(..) , selectionContext@@ -53,12 +61,10 @@ , PatchSelectionContext(allowSkipAll) ) import qualified Darcs.UI.SelectChanges as S-import Darcs.Repository.Flags ( UpdateWorking(..), DryRun(NoDryRun) )-import Darcs.Util.Path ( AbsolutePath ) +import Darcs.Util.Path ( AbsolutePath ) import Darcs.Util.Tree( Tree ) -import System.IO ( hPutStr, stderr ) tagDescription :: String tagDescription = "Name the current repository state for future reference."@@ -91,47 +97,6 @@ "The `darcs tag` command accepts the `--pipe` option, which behaves as\n" ++ "described in `darcs record`.\n" -tagBasicOpts :: DarcsOption a- (Maybe String- -> Maybe String- -> Bool- -> Maybe O.AskLongComment- -> Bool- -> Maybe String- -> a)-tagBasicOpts- = O.patchname- ^ O.author- ^ O.pipe- ^ O.askLongComment- ^ O.askdeps- ^ O.workingRepoDir--tagAdvancedOpts :: DarcsOption a (O.Compression -> O.UMask -> a)-tagAdvancedOpts = O.compress ^ O.umask--tagOpts :: DarcsOption a- (Maybe String- -> Maybe String- -> Bool- -> Maybe O.AskLongComment- -> Bool- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.Compression- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-tagOpts = tagBasicOpts `withStdOpts` tagAdvancedOpts- tag :: DarcsCommand [DarcsFlag] tag = DarcsCommand { commandProgramName = "darcs"@@ -142,7 +107,7 @@ , commandExtraArgHelp = ["[TAGNAME]"] , commandCommand = tagCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc tagAdvancedOpts , commandBasicOptions = odesc tagBasicOpts@@ -150,6 +115,16 @@ , commandCheckOptions = ocheck tagOpts , commandParseOptions = onormalise tagOpts }+ where+ tagBasicOpts+ = O.patchname+ ^ O.author+ ^ O.pipe+ ^ O.askLongComment+ ^ O.askDeps+ ^ O.repoDir+ tagAdvancedOpts = O.compress ^ O.umask+ tagOpts = tagBasicOpts `withStdOpts` tagAdvancedOpts filterNonInternal :: IsRepoType rt => PatchSet rt p wX wY -> PatchSet rt p wX wY filterNonInternal =@@ -159,25 +134,25 @@ tagCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () tagCmd _ opts args =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \(repository :: Repository rt 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)+ the_author <- getAuthor (author ? opts) (hasPipe opts) patches <- readRepo repository- tags <- getTags patches+ tags <- repoTags patches let nonInternalPatches = filterNonInternal patches Sealed chosenPatches <-- if AskDeps `elem` opts- then mapSeal (appendPSFL emptyPatchSet) <$> askAboutTagDepends opts (newset2FL nonInternalPatches)+ if O.askDeps ? opts+ then mapSeal (appendPSFL emptyPatchSet) <$> askAboutTagDepends opts (patchSet2FL nonInternalPatches) else return $ Sealed nonInternalPatches let deps = getUncovered chosenPatches (name, long_comment) <- get_name_log (NilFL :: FL (PrimOf p) wA wA) args tags myinfo <- patchinfo date name the_author long_comment let mypatch = infopatch myinfo NilFL- _ <- tentativelyAddPatch repository (compression opts) (verbosity opts) YesUpdateWorking+ _ <- tentativelyAddPatch repository (compress ? opts) (verbosity ? opts) YesUpdateWorking $ n2pia $ adddeps mypatch deps- finalizeRepositoryChanges repository YesUpdateWorking (compression opts)+ finalizeRepositoryChanges repository YesUpdateWorking (compress ? opts) putStrLn $ "Finished tagging patch '"++name++"'"- where get_name_log ::(Patchy prim, PrimPatch prim) => FL prim wA wA -> [String] -> [String] -> IO (String, [String])+ where get_name_log ::(PrimPatch prim) => FL prim wA wA -> [String] -> [String] -> IO (String, [String]) get_name_log nilFL a tags = do (name, comment, _) <- getLog (case parseFlags O.patchname opts of@@ -196,9 +171,6 @@ " already exists." return ("TAG " ++ name, comment) -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 -- internals: --@@ -217,7 +189,7 @@ -> IO (Sealed (FL (PatchInfoAnd rt p) wX)) askAboutTagDepends flags ps = do let opts = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = [] , S.interactive = True , S.selectDeps = O.PromptDeps
@@ -37,14 +37,9 @@ , nodefaults , putInfo , amInHashedRepository )-import Darcs.UI.Flags ( DarcsFlag( SetScriptsExecutable- , Linear- , Backoff- , Bisect- , LeaveTestDir- )- , useCache, verbosity )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Completion ( noArgs )+import Darcs.UI.Flags ( DarcsFlag, useCache, verbosity )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Patch.PatchInfoAnd ( hopefully ) import Darcs.Repository (@@ -66,24 +61,17 @@ , mapFL , mapRL_RL )-import Darcs.Patch.Conflict ( Conflict )-import Darcs.Patch.FileHunk ( IsHunk ) import Darcs.Patch.ApplyMonad ( ApplyMonad )-import Darcs.Patch.Apply ( ApplyState )-import Darcs.Patch.Format ( PatchListFormat )+import Darcs.Patch.Apply ( Apply, ApplyState ) import Darcs.Patch.Inspect ( PatchInspect )-import Darcs.Patch.Patchy ( Patchy- , Invert- , Apply- , ShowPatch- )+import Darcs.Patch.Invert ( Invert ) import Darcs.Patch ( RepoPatch , apply , description , invert ) import Darcs.Patch.Named.Wrapped ( WrappedNamed )-import Darcs.Patch.Set ( newset2RL )+import Darcs.Patch.Set ( patchSet2RL ) import Darcs.Util.Printer ( putDocLn , text )@@ -131,31 +119,6 @@ ,"break the test is found at random." ] -testBasicOpts :: DarcsOption a- (O.TestStrategy -> O.LeaveTestDir -> Maybe String -> a)-testBasicOpts = O.testStrategy ^ O.leaveTestDir ^ O.workingRepoDir--testAdvancedOpts :: DarcsOption a (O.SetScriptsExecutable -> a)-testAdvancedOpts = O.setScriptsExecutable--testOpts :: DarcsOption a- (O.TestStrategy- -> O.LeaveTestDir- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.SetScriptsExecutable- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-testOpts = testBasicOpts `withStdOpts` testAdvancedOpts- test :: DarcsCommand [DarcsFlag] test = DarcsCommand { commandProgramName = "darcs"@@ -166,7 +129,7 @@ , commandExtraArgHelp = ["[[INITIALIZATION]", "COMMAND]"] , commandCommand = testCommand , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc testAdvancedOpts , commandBasicOptions = odesc testBasicOpts@@ -174,6 +137,10 @@ , commandCheckOptions = ocheck testOpts , commandParseOptions = onormalise testOpts }+ where+ testBasicOpts = O.testStrategy ^ O.leaveTestDir ^ O.repoDir+ testAdvancedOpts = O.setScriptsExecutable+ testOpts = testBasicOpts `withStdOpts` testAdvancedOpts -- | Functions defining a strategy for executing a test type Strategy = forall rt p wX wY@@ -186,11 +153,11 @@ testCommand :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () testCommand _ opts args =- withRepository (useCache opts) $ RepoJob $ \repository -> do+ withRepository (useCache ? opts) $ RepoJob $ \repository -> do patches <- readRepo repository (init,testCmd) <- case args of [] ->- do t <- getTest (verbosity opts)+ do t <- getTest (verbosity ? opts) return (return ExitSuccess, t) [cmd] -> do putStrLn $ "Using test command:\n"++cmd@@ -200,21 +167,22 @@ putStrLn $ "Using test command:\n"++cmd return (system init, system cmd) _ -> fail "Test expects zero to two arguments."- let wd = if LeaveTestDir `elem` opts then withPermDir else withTempDir+ let wd = case O.leaveTestDir ? opts of+ O.YesLeaveTestDir -> withPermDir+ O.NoLeaveTestDir -> withTempDir withRecorded repository (wd "testing") $ \_ -> do- when (SetScriptsExecutable `elem` opts) setScriptsExecutable+ when (O.yes (O.setScriptsExecutable ? opts)) setScriptsExecutable _ <- init putInfo opts $ text "Running test...\n" testResult <- testCmd- let track = chooseStrategy opts- track opts testCmd testResult (mapRL_RL hopefully . newset2RL $ patches)+ let track = chooseStrategy (O.testStrategy ? opts)+ track opts testCmd testResult (mapRL_RL hopefully . patchSet2RL $ patches) -chooseStrategy :: [DarcsFlag] -> Strategy-chooseStrategy opts- | Bisect `elem` opts = trackBisect- | Linear `elem` opts = trackLinear- | Backoff `elem` opts = trackBackoff- | otherwise = oneTest+chooseStrategy :: O.TestStrategy -> Strategy+chooseStrategy O.Bisect = trackBisect+chooseStrategy O.Linear = trackLinear+chooseStrategy O.Backoff = trackBackoff+chooseStrategy O.Once = oneTest -- | test only the last recorded state oneTest :: Strategy@@ -229,7 +197,7 @@ trackLinear opts testCmd (ExitFailure _) (ps:<:p) = do let ip = invert p safeApply ip- when (SetScriptsExecutable `elem` opts) $ setScriptsExecutablePatches ip+ when (O.yes (O.setScriptsExecutable ? opts)) $ setScriptsExecutablePatches ip putStrLn "Trying without the patch:" putDocLn $ description ip hFlush stdout@@ -260,7 +228,7 @@ case splitAtRL n ahead of ( ahead' :> skipped' ) -> do unapplyRL skipped'- when (SetScriptsExecutable `elem` opts) $ setScriptsExecutablePatches skipped'+ when (O.yes (O.setScriptsExecutable ? opts)) $ setScriptsExecutablePatches skipped' testResult <- testCmd case testResult of ExitFailure _ ->@@ -300,13 +268,13 @@ type BisectState = (Int, Int) -- | Create Bisect PatchTree from the RL-patchTreeFromRL :: (Patchy p) => RL p wX wY -> PatchTree p wX wY+patchTreeFromRL :: RL p wX wY -> PatchTree p wX wY patchTreeFromRL (NilRL :<: l) = Leaf l patchTreeFromRL xs = case splitAtRL (lengthRL xs `div` 2) xs of (r :> l) -> Fork (patchTreeFromRL l) (patchTreeFromRL r) -- | Convert PatchTree back to RL-patchTree2RL :: (Patchy p) => PatchTree p wX wY -> RL p wX wY+patchTree2RL :: PatchTree p wX wY -> RL p wX wY patchTree2RL (Leaf p) = NilRL :<: p patchTree2RL (Fork l r) = patchTree2RL r +<+ patchTree2RL l @@ -334,31 +302,29 @@ putStrLn "Last recent patch that fails the test (assuming monotony in the given range):" putDocLn (description p) -jumpHalfOnRight :: (IsHunk p, Conflict p,- PatchListFormat p, ShowPatch p, PatchInspect p,- Patchy p, ApplyMonad (ApplyState p) DefaultIO)+jumpHalfOnRight :: (Invert p, Apply p, PatchInspect p,+ ApplyMonad (ApplyState p) DefaultIO) => [DarcsFlag] -> PatchTree p wX wY -> IO () jumpHalfOnRight opts l = do unapplyRL ps- when (SetScriptsExecutable `elem` opts) $ setScriptsExecutablePatches ps+ when (O.yes (O.setScriptsExecutable ? opts)) $ setScriptsExecutablePatches ps where ps = patchTree2RL l -jumpHalfOnLeft :: (IsHunk p, Conflict p,- PatchListFormat p, ShowPatch p, PatchInspect p,- Patchy p, ApplyMonad (ApplyState p) DefaultIO)+jumpHalfOnLeft :: (Apply p, PatchInspect p,+ ApplyMonad (ApplyState p) DefaultIO) => [DarcsFlag] -> PatchTree p wX wY -> IO () jumpHalfOnLeft opts r = do applyRL p- when (SetScriptsExecutable `elem` opts) $ setScriptsExecutablePatches p+ when (O.yes (O.setScriptsExecutable ? opts)) $ setScriptsExecutablePatches p where p = patchTree2RL r -applyRL :: (Invert p, ShowPatch p, Apply p, ApplyMonad (ApplyState p) DefaultIO)+applyRL :: (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 (ApplyState p) DefaultIO)+unapplyRL :: (Invert 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 (ApplyState p) DefaultIO)+safeApply :: (Apply p, ApplyMonad (ApplyState p) DefaultIO) => p wX wY -> IO () safeApply p = runDefault (apply p) `catch` \(msg :: IOException) -> fail $ "Bad patch:\n" ++ show msg
@@ -27,8 +27,9 @@ import Darcs.Util.File ( withCurrentDirectory ) import Darcs.Util.Exception ( prettyException ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInRepository )+import Darcs.UI.Completion ( noArgs ) import Darcs.UI.Flags ( DarcsFlag )-import Darcs.UI.Options ( DarcsOption, oid, odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Options ( oid, odesc, ocheck, onormalise, defaultFlags ) import qualified Darcs.UI.Options.All as O import Darcs.Util.Progress ( setProgressMode ) import Darcs.Util.Global ( darcsdir )@@ -52,24 +53,6 @@ "who do not run ssh-agent will be prompted for the ssh password tens or\n" ++ "hundreds of times!\n" -transferModeBasicOpts :: DarcsOption a (Maybe String -> a)-transferModeBasicOpts = O.workingRepoDir--transferModeOpts :: DarcsOption a- (Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-transferModeOpts = transferModeBasicOpts `withStdOpts` oid- transferMode :: DarcsCommand [DarcsFlag] transferMode = DarcsCommand { commandProgramName = "darcs"@@ -78,7 +61,7 @@ , commandDescription = transferModeDescription , commandExtraArgs = 0 , commandExtraArgHelp = []- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandCommand = transferModeCmd , commandPrereq = amInRepository , commandArgdefaults = nodefaults@@ -88,6 +71,9 @@ , commandCheckOptions = ocheck transferModeOpts , commandParseOptions = onormalise transferModeOpts }+ where+ transferModeBasicOpts = O.repoDir+ transferModeOpts = transferModeBasicOpts `withStdOpts` oid transferModeCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () transferModeCmd _ _ _ = do setProgressMode False
@@ -15,7 +15,7 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, OverloadedStrings #-}+{-# LANGUAGE OverloadedStrings #-} module Darcs.UI.Commands.Unrecord ( unrecord@@ -31,17 +31,16 @@ import Prelude hiding ( (^) ) import Control.Exception ( catch, IOException )-import Control.Monad ( when, unless )-import Data.Maybe( isJust, mapMaybe )-import Data.List ( intercalate )+import Control.Monad ( when )+import Data.Maybe( isJust ) import Darcs.Util.Tree( Tree ) import System.Exit ( exitSuccess ) 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 )-import Darcs.Patch.Match ( firstMatch, matchFirstPatchset, matchAPatchread, MatchFlag )+import Darcs.Patch.Depends ( findCommonWithThem, patchSetUnion )+import Darcs.Patch.Match ( firstMatch, matchFirstPatchset, matchAPatch, MatchFlag ) import Darcs.Patch.PatchInfoAnd ( hopefully, patchDesc ) import Darcs.Patch.Set ( PatchSet(..), Tagged(..), appendPSFL, Origin, SealedPatchSet )@@ -59,22 +58,23 @@ import Darcs.Util.Lock( writeDocBinFile ) import Darcs.Repository.Prefs ( getDefaultRepoPath ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, commandAlias- , putVerbose, printDryRunMessageAndExit+ , putVerbose , setEnvDarcsPatches, amInHashedRepository , putInfo )-import Darcs.UI.Commands.Util ( getUniqueDPatchName )+import Darcs.UI.Commands.Util ( getUniqueDPatchName, printDryRunMessageAndExit )+import Darcs.UI.Completion ( noArgs ) import Darcs.UI.Flags- ( doReverse, compression, verbosity, getOutput- , useCache, dryRun, umask, DarcsFlag ( NotInRemote ), minimize- , diffAlgorithm, hasXmlOutput, hasSummary, isInteractive, selectDeps )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags )+ ( DarcsFlag, changesReverse, compress, verbosity, getOutput+ , useCache, dryRun, umask, minimize+ , diffAlgorithm, xmlOutput, isInteractive, selectDeps )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) ) import Darcs.UI.Options.All ( notInRemoteFlagName ) import qualified Darcs.UI.Options.All as O 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, vcat, (<>), (<+>), ($$) ) import Darcs.Util.Progress ( debugMessage ) unrecordDescription :: String@@ -91,41 +91,6 @@ , "possibility that another user may have already pulled the patch." ] -unrecordBasicOpts :: DarcsOption a- ([MatchFlag] -> O.SelectDeps -> Maybe Bool -> Maybe String -> a)-unrecordBasicOpts- = O.matchSeveralOrLast- ^ O.selectDeps- ^ O.interactive -- True- ^ O.workingRepoDir--unrecordAdvancedOpts :: DarcsOption a (O.Compression -> O.UMask -> Bool -> a)-unrecordAdvancedOpts- = O.compress- ^ O.umask- ^ O.changesReverse--unrecordOpts :: DarcsOption a- ([MatchFlag]- -> O.SelectDeps- -> Maybe Bool- -> Maybe String- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.Compression- -> O.UMask- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-unrecordOpts = unrecordBasicOpts `withStdOpts` unrecordAdvancedOpts- unrecord :: DarcsCommand [DarcsFlag] unrecord = DarcsCommand { commandProgramName = "darcs"@@ -136,7 +101,7 @@ , commandExtraArgHelp = [] , commandCommand = unrecordCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc unrecordAdvancedOpts , commandBasicOptions = odesc unrecordBasicOpts@@ -144,20 +109,27 @@ , commandCheckOptions = ocheck unrecordOpts , commandParseOptions = onormalise unrecordOpts }+ where+ unrecordBasicOpts+ = O.notInRemote+ ^ O.matchSeveralOrLast+ ^ O.selectDeps+ ^ O.interactive -- True+ ^ O.repoDir+ unrecordAdvancedOpts+ = O.compress+ ^ O.umask+ ^ O.changesReverse+ unrecordOpts = unrecordBasicOpts `withStdOpts` unrecordAdvancedOpts unrecordCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () unrecordCmd _ opts _ =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $+ withRepoLock NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts) $ RepoJob $ \repository -> do- allpatches <- readRepo repository- let matchFlags = parseFlags O.matchSeveralOrLast opts- (_ :> patches) <- return $- if firstMatch matchFlags- then getLastPatches matchFlags allpatches- else matchingHead matchFlags allpatches- let direction = if doReverse opts then Last else LastReversed+ (_ :> removal_candidates) <- preselectPatches opts repository+ let direction = if changesReverse ? opts then Last else LastReversed context = selectionContext direction "unrecord" (patchSelOpts opts) Nothing Nothing- (_ :> to_unrecord) <- runSelection patches context+ (_ :> to_unrecord) <- runSelection removal_candidates context when (nullFL to_unrecord) $ do putInfo opts "No patches selected!" exitSuccess@@ -165,9 +137,9 @@ text "About to write out (potentially) modified patches..." setEnvDarcsPatches to_unrecord invalidateIndex repository- _ <- tentativelyRemovePatches repository (compression opts)+ _ <- tentativelyRemovePatches repository (compress ? opts) YesUpdateWorking to_unrecord- finalizeRepositoryChanges repository YesUpdateWorking (compression opts)+ finalizeRepositoryChanges repository YesUpdateWorking (compress ? opts) putInfo opts "Finished unrecording." getLastPatches :: (IsRepoType rt, RepoPatch p) => [MatchFlag] -> PatchSet rt p Origin wR@@ -218,68 +190,6 @@ , "later to your repository with `darcs apply`." ] -obliterateBasicOpts :: DarcsOption a- ([Maybe String]- -> [MatchFlag]- -> O.SelectDeps- -> Maybe Bool- -> Maybe String- -> Maybe O.Summary- -> Maybe O.Output- -> Bool- -> O.DiffAlgorithm- -> O.DryRun- -> O.XmlOutput- -> a)-obliterateBasicOpts- = O.notInRemote- ^ O.matchSeveralOrLast- ^ O.selectDeps- ^ O.interactive- ^ O.workingRepoDir- ^ O.summary- ^ O.output- ^ O.minimize- ^ O.diffAlgorithm- ^ O.dryRunXml--obliterateAdvancedOpts :: DarcsOption a- (O.Compression -> O.UseIndex -> O.UMask -> Bool -> a)-obliterateAdvancedOpts- = O.compress- ^ O.useIndex- ^ O.umask- ^ O.changesReverse--obliterateOpts :: DarcsOption a- ([Maybe String]- -> [MatchFlag]- -> O.SelectDeps- -> Maybe Bool- -> Maybe String- -> Maybe O.Summary- -> Maybe O.Output- -> Bool- -> O.DiffAlgorithm- -> DryRun- -> O.XmlOutput- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.Compression- -> UseIndex- -> O.UMask- -> Bool- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-obliterateOpts = obliterateBasicOpts `withStdOpts` obliterateAdvancedOpts- obliterate :: DarcsCommand [DarcsFlag] obliterate = DarcsCommand { commandProgramName = "darcs"@@ -290,7 +200,7 @@ , commandExtraArgHelp = [] , commandCommand = obliterateCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc obliterateAdvancedOpts , commandBasicOptions = odesc obliterateBasicOpts@@ -298,13 +208,28 @@ , commandCheckOptions = ocheck obliterateOpts , commandParseOptions = onormalise obliterateOpts }+ where+ obliterateBasicOpts+ = O.notInRemote+ ^ O.matchSeveralOrLast+ ^ O.selectDeps+ ^ O.interactive+ ^ O.repoDir+ ^ O.summary+ ^ O.output+ ^ O.minimize+ ^ O.diffAlgorithm+ ^ O.dryRunXml+ obliterateAdvancedOpts+ = O.compress+ ^ O.useIndex+ ^ O.umask+ ^ O.changesReverse+ obliterateOpts = obliterateBasicOpts `withStdOpts` obliterateAdvancedOpts obliterateCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () obliterateCmd = genericObliterateCmd "obliterate" -data NotInRemoteLocation = NotInDefaultRepo- | NotInRemotePath String- -- | genericObliterateCmd is the function that executes the "obliterate" and -- "unpull" commands. The first argument is the name under which the command is -- invoked (@unpull@ or @obliterate@).@@ -314,31 +239,17 @@ -> [String] -> IO () genericObliterateCmd cmdname _ opts _ =- let cacheOpt = useCache opts- verbOpt = verbosity opts- in withRepoLock (dryRun opts) cacheOpt YesUpdateWorking (umask 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 -- command does not take that option (yet)- pend <- unrecordedChanges (UseIndex, ScanKnown, diffAlgorithm opts) repository Nothing- allpatches <- readRepo repository- let collectNotIns (NotInRemote nir) = case nir of- Just p -> Just $ NotInRemotePath p- Nothing -> Just NotInDefaultRepo- collectNotIns _ = Nothing- notIns = mapMaybe collectNotIns opts- (auto_kept :> removal_candidates) <- case notIns of- [] -> do- let matchFlags = parseFlags O.matchSeveralOrLast opts- return $ if firstMatch matchFlags- then getLastPatches matchFlags allpatches- else matchingHead matchFlags allpatches- nirs -> do- (Sealed thems) <-- getNotInRemotePatches verbOpt cacheOpt repository nirs- return $ findCommonWithThem allpatches thems+ pend <- unrecordedChanges (UseIndex, ScanKnown, diffAlgorithm ? opts)+ O.NoLookForMoves O.NoLookForReplaces repository Nothing+ (auto_kept :> removal_candidates) <- preselectPatches opts repository - let direction = if doReverse opts then Last else LastReversed+ let direction = if changesReverse ? opts then Last else LastReversed context = selectionContext direction cmdname (patchSelOpts opts) Nothing Nothing (kept :> removed) <- runSelection removal_candidates context@@ -352,9 +263,9 @@ Just (_ :> p_after_pending) -> do printDryRunMessageAndExit "obliterate" verbOpt- (hasSummary O.NoSummary opts)- (dryRun opts)- (hasXmlOutput opts)+ (O.summary ? opts)+ (dryRun ? opts)+ (xmlOutput ? opts) (isInteractive True opts) removed setEnvDarcsPatches removed@@ -362,11 +273,11 @@ savetoBundle opts (auto_kept `appendPSFL` kept) removed invalidateIndex repository _ <- tentativelyRemovePatches repository- (compression opts) YesUpdateWorking removed+ (compress ? opts) YesUpdateWorking removed tentativelyAddToPending repository YesUpdateWorking $ invert $ effect removed finalizeRepositoryChanges repository- YesUpdateWorking (compression opts)+ YesUpdateWorking (compress ? opts) debugMessage "Applying patches to working directory..." _ <- applyToWorking repository verbOpt (invert p_after_pending)@@ -376,30 +287,27 @@ putInfo opts $ "Finished" <+> text (presentParticiple cmdname) <> "." -- | Get the union of the set of patches in each specified location-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+remotePatches :: (IsRepoType rt, RepoPatch p)+ => [DarcsFlag]+ -> Repository rt p wX wU wT -> [O.NotInRemote]+ -> IO (SealedPatchSet rt p Origin)+remotePatches opts repository nirs = do nirsPaths <- mapM getNotInRemotePath nirs- newsetUnion `fmap` mapM readNir nirsPaths+ putInfo opts $ "Determining patches not in" <+> pluralExtra nirsPaths $$+ itemize nirsPaths+ patchSetUnion `fmap` mapM readNir nirsPaths where- toName (NotInRemotePath s) = "'" ++ s ++ "'"- toName NotInDefaultRepo = "Default push/pull repo"-- pluralExtra = if length names > 1 then " any of" else ""- names = intercalate "\n" $ map ((leader ++) . toName) nirs- leader = " - "+ pluralExtra names = if length names > 1 then "any of" else mempty+ itemize = vcat . map (text . (" - " ++)) readNir n = do- r <- identifyRepositoryFor repository cacheOpt n+ r <- identifyRepositoryFor repository (useCache ? opts) n rps <- readRepo r return $ seal rps - getNotInRemotePath (NotInRemotePath p) = return p- getNotInRemotePath NotInDefaultRepo = do+ getNotInRemotePath :: O.NotInRemote -> IO String+ getNotInRemotePath (O.NotInRemotePath p) = return p+ getNotInRemotePath O.NotInDefaultRepo = do defaultRepo <- getDefaultRepoPath let err = fail $ "No default push/pull repo configured, please pass a " ++ "repo name to --" ++ notInRemoteFlagName@@ -418,7 +326,7 @@ 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+ | or (mapRL (matchAPatch matchFlags) x) = contextPatches s mh (PatchSet (ts :<: Tagged t _ ps) x) = case mh (PatchSet ts (ps :<: t)) of (start :> patches) -> start :> patches +<+ x@@ -428,7 +336,7 @@ -> 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)+ bundle <- if not (minimize ? opts) then genFullBundle else do putInfo opts "Minimizing context, to generate bundle with full context hit ctrl-C..." ( case minContext kept removed of@@ -441,12 +349,33 @@ useAbsoluteOrStd writeDocBinFile putDoc outname bundle savetoBundle _ _ NilFL = return () +preselectPatches+ :: (IsRepoType rt, RepoPatch p)+ => [DarcsFlag]+ -> Repository rt p wR wU wT+ -> IO ((PatchSet rt p :> FL (PatchInfoAnd rt p)) Origin wR)+preselectPatches opts repo = do+ allpatches <- readRepo repo+ let matchFlags = parseFlags O.matchSeveralOrLast opts+ case O.notInRemote ? opts of+ [] -> do+ return $+ if firstMatch matchFlags+ then getLastPatches matchFlags allpatches+ else matchingHead matchFlags allpatches+ -- FIXME what about match options when we have --not-in-remote?+ -- It looks like they are simply ignored.+ nirs -> do+ (Sealed thems) <-+ remotePatches opts repo nirs+ return $ findCommonWithThem allpatches thems+ patchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions patchSelOpts flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = parseFlags O.matchSeveralOrLast flags , S.interactive = isInteractive True flags- , S.selectDeps = selectDeps flags- , S.summary = hasSummary O.NoSummary flags+ , S.selectDeps = selectDeps ? flags+ , S.summary = O.summary ? flags , S.withContext = O.NoContext }
@@ -15,29 +15,25 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP, GADTs #-}-- 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 Darcs.Util.Tree( Tree ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, nodefaults, amInHashedRepository )+import Darcs.UI.Completion ( noArgs ) import Darcs.UI.Flags- ( diffingOpts, verbosity, useCache, umask, compression, diffAlgorithm- , isInteractive, isUnified )+ ( diffingOpts, verbosity, useCache, umask, compress, diffAlgorithm+ , isInteractive, withContext ) import Darcs.Repository.Flags ( UseIndex(..), ScanKnown (..), Reorder(..), AllowConflicts(..), ExternalMerge(..) , WantGuiPause(..), UpdateWorking(..), DryRun(NoDryRun) ) import Darcs.UI.Flags ( DarcsFlag )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags )+import Darcs.UI.Options ( (^), odesc, ocheck, onormalise, defaultFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.Repository ( SealedPatchSet, Repository, withRepoLock, RepoJob(..), unrevertUrl, considerMergeToWorking,@@ -48,6 +44,7 @@ import Darcs.Patch ( IsRepoType, RepoPatch, PrimOf, commute, fromPrims ) import Darcs.Patch.Apply( ApplyState ) import Darcs.Patch.Named.Wrapped ( namepatch )+import Darcs.Patch.Rebase ( dropAnyRebase ) import Darcs.Patch.Set ( Origin ) import Darcs.Patch.Witnesses.Sealed ( Sealed(Sealed) ) import Darcs.Patch.Witnesses.Ordered ( FL(..), (:>)(..), (+>+) )@@ -67,7 +64,6 @@ import Darcs.Util.SignalHandler ( withSignalsBlocked ) import Darcs.Util.Progress ( debugMessage ) import Darcs.Util.Path ( AbsolutePath )-#include "impossible.h" unrevertDescription :: String unrevertDescription =@@ -83,51 +79,14 @@ "took place. Darcs will ask for confirmation before executing an\n" ++ "interactive command that will DEFINITELY prevent unreversion.\n" -unrevertBasicOpts :: DarcsOption a- (O.UseIndex- -> Maybe Bool- -> Maybe String- -> O.WithContext- -> O.DiffAlgorithm- -> a)-unrevertBasicOpts- = O.useIndex- ^ O.interactive -- True- ^ O.workingRepoDir- ^ O.withContext- ^ O.diffAlgorithm--unrevertAdvancedOpts :: DarcsOption a (O.UMask -> a)-unrevertAdvancedOpts = O.umask--unrevertOpts :: DarcsOption a- (UseIndex- -> Maybe Bool- -> Maybe String- -> O.WithContext- -> O.DiffAlgorithm- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UMask- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-unrevertOpts = unrevertBasicOpts `withStdOpts` unrevertAdvancedOpts- patchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions patchSelOpts flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = [] , S.interactive = isInteractive True flags , S.selectDeps = O.PromptDeps -- option not supported, use default , S.summary = O.NoSummary -- option not supported, use default- , S.withContext = isUnified flags+ , S.withContext = withContext ? flags } unrevert :: DarcsCommand [DarcsFlag]@@ -140,7 +99,7 @@ , commandExtraArgHelp = [] , commandCommand = unrevertCmd , commandPrereq = amInHashedRepository- , commandGetArgPossibilities = return []+ , commandCompleteArgs = noArgs , commandArgdefaults = nodefaults , commandAdvancedOptions = odesc unrevertAdvancedOpts , commandBasicOptions = odesc unrevertBasicOpts@@ -148,27 +107,37 @@ , commandCheckOptions = ocheck unrevertOpts , commandParseOptions = onormalise unrevertOpts }+ where+ unrevertBasicOpts+ = O.useIndex+ ^ O.interactive -- True+ ^ O.repoDir+ ^ O.withContext+ ^ O.diffAlgorithm+ unrevertAdvancedOpts = O.umask+ unrevertOpts = unrevertBasicOpts `withStdOpts` unrevertAdvancedOpts unrevertCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () unrevertCmd _ opts [] =- withRepoLock NoDryRun (useCache opts) YesUpdateWorking (umask opts) $ RepoJob $ \repository -> do+ withRepoLock NoDryRun (useCache ? opts) YesUpdateWorking (umask ? opts) $ RepoJob $ \repository -> do us <- readRepo repository Sealed them <- unrevertPatchBundle repository recorded <- readRecorded repository- unrecorded <- unrecordedChanges (diffingOpts opts {- always ScanKnown here -}) repository Nothing+ unrecorded <- unrecordedChanges (diffingOpts opts {- always ScanKnown here -})+ O.NoLookForMoves O.NoLookForReplaces repository Nothing Sealed h_them <- return $ mergeThem us them Sealed pw <- considerMergeToWorking repository "unrevert" YesAllowConflictsAndMark YesUpdateWorking NoExternalMerge NoWantGuiPause- (compression opts) (verbosity opts) NoReorder- ( UseIndex, ScanKnown, diffAlgorithm opts )+ (compress ? opts) (verbosity ? opts) NoReorder+ ( UseIndex, ScanKnown, diffAlgorithm ? opts ) NilFL h_them let context = selectionContextPrim First "unrevert" (patchSelOpts opts) Nothing Nothing (Just recorded) (p :> skipped) <- runSelection pw context tentativelyAddToPending repository YesUpdateWorking p withSignalsBlocked $- do finalizeRepositoryChanges repository YesUpdateWorking (compression opts)- _ <- applyToWorking repository (verbosity opts) p `catch` \(e :: IOException) ->+ do finalizeRepositoryChanges repository YesUpdateWorking (compress ? opts)+ _ <- applyToWorking repository (verbosity ? opts) p `catch` \(e :: IOException) -> fail ("Error applying unrevert to working directory...\n" ++ show e) debugMessage "I'm about to writeUnrevert."@@ -187,7 +156,7 @@ _ -> exitSuccess writeUnrevert repository NilFL recorded pend Just (p' :> _) -> do- rep <- readRepo repository+ rep <- dropAnyRebase <$> readRepo repository date <- getIsoDateTime np <- namepatch date "unrevert" "anon" [] (fromRepoPrims repository p') bundle <- makeBundleN (Just recorded) rep (np :>: NilFL)
@@ -15,49 +15,76 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module Darcs.UI.Commands.Util ( announceFiles , filterExistingPaths , testTentativeAndMaybeExit+ , printDryRunMessageAndExit , getUniqueRepositoryName , getUniqueDPatchName+ , expandDirs+ , doesDirectoryReallyExist+ , checkUnrelatedRepos+ , repoTags ) where -import Control.Monad ( unless )+import Control.Monad ( when, unless )+import Data.Maybe ( catMaybes ) import Prelude () import Darcs.Prelude -import System.Exit ( ExitCode(..), exitWith )+import System.Exit ( ExitCode(..), exitWith, exitSuccess )+import System.FilePath.Posix ( (</>) )+import System.Posix.Files ( isDirectory ) -import Darcs.Util.Tree.Monad ( virtualTreeIO, exists )-import Darcs.Util.Tree ( Tree )+import Darcs.Patch ( RepoPatch, xmlSummary )+import Darcs.Patch.Depends ( areUnrelatedRepos )+import Darcs.Patch.Info ( toXml, piTag )+import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info, hopefullyM )+import Darcs.Patch.Set ( PatchSet(..), patchSetfMap )+import Darcs.Patch.Witnesses.Ordered ( FL, mapFL ) -import Darcs.Patch ( RepoPatch ) import Darcs.Repository ( Repository, readRecorded, testTentative )-import Darcs.Repository.State ( readUnrecordedFiltered )+import Darcs.Repository.State+ ( readUnrecordedFiltered, readWorking, restrictBoring+ , TreeFilter(..), applyTreeFilter+ ) import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch.Bundle ( patchFilename )++import Darcs.UI.PrintPatch ( showFriendly ) import Darcs.UI.Options.All ( Verbosity(..), SetScriptsExecutable, TestChanges (..)- , RunTest(..), LeaveTestDir(..), UseIndex, ScanKnown(..) )+ , RunTest(..), LeaveTestDir(..), UseIndex, ScanKnown(..)+ , Summary(..), DryRun(..), XmlOutput(..), LookForMoves+ )+ import Darcs.Util.Exception ( clarifyErrors )+import Darcs.Util.File ( getFileStatus, withCurrentDirectory ) import Darcs.Util.Path- ( SubPath, toFilePath, getUniquePathName, floatPath )-import Darcs.Util.Printer ( putDocLn, text, (<>), (<+>) )-import Darcs.Util.Prompt ( PromptConfig(..), promptChar )+ ( SubPath, toFilePath, getUniquePathName, floatPath+ , simpleSubPath, toPath, anchorPath+ )+import Darcs.Util.Printer+ ( text, (<>), (<+>), hsep, ($$), vcat, vsep+ , putDocLn, insertBeforeLastline, prefix+ )+import Darcs.Util.Prompt ( PromptConfig(..), promptChar, promptYorn ) import Darcs.Util.Text ( pathlist )+import Darcs.Util.Tree.Monad ( virtualTreeIO, exists )+import Darcs.Util.Tree ( Tree )+import qualified Darcs.Util.Tree as Tree + 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 rt p wR wU wT+testTentativeAndMaybeExit :: Repository rt p wR wU wT -> Verbosity -> TestChanges -> SetScriptsExecutable@@ -78,6 +105,47 @@ yn <- promptChar (PromptConfig prompt "yn" [] (Just 'n') []) unless (yn == 'y') doExit +-- | @'printDryRunMessageAndExit' action flags patches@ prints a string+-- representing the action that would be taken if the @--dry-run@ option had+-- not been passed to darcs. Then darcs exits successfully. @action@ is the+-- name of the action being taken, like @\"push\"@ @flags@ is the list of flags+-- which were sent to darcs @patches@ is the sequence of patches which would be+-- touched by @action@.+printDryRunMessageAndExit :: (RepoPatch p, ApplyState p ~ Tree)+ => String+ -> Verbosity -> Summary -> DryRun -> XmlOutput+ -> Bool -- interactive+ -> FL (PatchInfoAnd rt p) wX wY+ -> IO ()+printDryRunMessageAndExit action v s d x interactive patches = do+ when (d == YesDryRun) $ do+ putInfoX $ hsep [ "Would", text action, "the following changes:" ]+ putDocLn put_mode+ putInfoX $ text ""+ putInfoX $ text "Making no changes: this is a dry run."+ exitSuccess+ when (not interactive && s == YesSummary) $ do+ putInfoX $ hsep [ "Will", text action, "the following changes:" ]+ putDocLn put_mode+ where+ put_mode = if x == YesXml+ then text "<patches>" $$+ vcat (mapFL (indent . xml_info s) patches) $$+ text "</patches>"+ else vsep $ mapFL (showFriendly v s) patches++ putInfoX = if x == YesXml then const (return ()) else putDocLn++ xml_info YesSummary = xml_with_summary+ xml_info NoSummary = toXml . info++ xml_with_summary hp+ | Just p <- hopefullyM hp = insertBeforeLastline (toXml $ info hp)+ (indent $ xmlSummary p)+ xml_with_summary hp = toXml (info hp)++ indent = prefix " "+ -- | 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.@@ -88,11 +156,12 @@ -> Verbosity -> UseIndex -> ScanKnown+ -> LookForMoves -> [SubPath] -> IO ([SubPath],[SubPath])-filterExistingPaths repo verb useidx scan paths = do+filterExistingPaths repo verb useidx scan lfm paths = do pristine <- readRecorded repo- working <- readUnrecordedFiltered repo useidx scan (Just paths)+ working <- readUnrecordedFiltered repo useidx scan lfm (Just paths) let filepaths = map toFilePath paths check = virtualTreeIO $ mapM (exists . floatPath) filepaths (in_pristine, _) <- check pristine@@ -122,3 +191,46 @@ buildMsg n = "Directory or file '"++ name ++ "' already exists, creating dpatch as '"++ n ++"'"++-- | For each directory in the list of 'SubPath's, add all paths+-- under that directory to the list. If the first argument is 'True', then+-- include even boring files.+--+-- This is used by the add and remove commands to handle the --recursive option.+expandDirs :: Bool -> [SubPath] -> IO [SubPath]+expandDirs includeBoring subpaths =+ do+ boringFilter <-+ if includeBoring+ then return (TreeFilter id)+ else restrictBoring Tree.emptyTree+ fmap (map (fromJust . simpleSubPath)) $+ concat `fmap` mapM (expandOne boringFilter . toPath) subpaths+ where+ expandOne boringFilter "" = listFiles boringFilter+ expandOne boringFilter f = do+ isdir <- doesDirectoryReallyExist f+ if not isdir+ then return [f]+ else do+ fs <- withCurrentDirectory f (listFiles boringFilter)+ return $ f: map (f </>) fs+ listFiles boringFilter = do+ working <- applyTreeFilter boringFilter <$> readWorking+ return $ map (anchorPath "" . fst) $ Tree.list working++doesDirectoryReallyExist :: FilePath -> IO Bool+doesDirectoryReallyExist f = maybe False isDirectory `fmap` getFileStatus f++checkUnrelatedRepos :: RepoPatch p+ => Bool+ -> PatchSet rt p wStart wX+ -> PatchSet rt p wStart wY+ -> IO ()+checkUnrelatedRepos allowUnrelatedRepos us them =+ when ( not allowUnrelatedRepos && areUnrelatedRepos us them ) $+ do confirmed <- promptYorn "Repositories seem to be unrelated. Proceed?"+ unless confirmed $ putStrLn "Cancelled." >> exitSuccess++repoTags :: PatchSet rt p wX wY -> IO [String]+repoTags ps = catMaybes `fmap` patchSetfMap (return . piTag . info) ps
@@ -30,47 +30,42 @@ import Control.Monad ( forM ) import Control.Monad.State.Strict( gets ) -import qualified Data.ByteString.Char8 as BSC-import Data.Char ( toLower )--import Darcs.Util.Tree.Monad- ( withDirectory, fileExists, directoryExists- , virtualTreeMonad, currentDirectory- , TreeMonad )-import qualified Darcs.Util.Tree.Monad as HS ( exists, tree )+import qualified Darcs.Util.Tree.Monad as TM+ ( TreeMonad, withDirectory, fileExists, directoryExists+ , virtualTreeMonad, currentDirectory, exists, tree ) import Darcs.Util.Tree ( Tree, listImmediate, findTree ) import Darcs.Util.Path- ( AnchoredPath(..), Name(..), floatPath )+ ( AnchoredPath(..), floatPath, eqAnycase ) -treeHasAnycase :: (Functor m, Monad m)+treeHasAnycase :: Monad m => Tree m -> FilePath -> m Bool treeHasAnycase tree path =- fst `fmap` virtualTreeMonad (existsAnycase $ floatPath path) tree+ fst `fmap` TM.virtualTreeMonad (existsAnycase $ floatPath path) tree -existsAnycase :: (Functor m, Monad m)+existsAnycase :: Monad m => AnchoredPath- -> TreeMonad m Bool+ -> TM.TreeMonad m Bool existsAnycase (AnchoredPath []) = return True-existsAnycase (AnchoredPath (Name x:xs)) = do- wd <- currentDirectory- Just tree <- gets (flip findTree wd . HS.tree)- let subs = [ AnchoredPath [Name n] | (Name n, _) <- listImmediate tree,- BSC.map toLower n == BSC.map toLower x ]+existsAnycase (AnchoredPath (x:xs)) = do+ wd <- TM.currentDirectory+ Just tree <- gets (flip findTree wd . TM.tree)+ let subs = [ AnchoredPath [n] | (n, _) <- listImmediate tree,+ eqAnycase n x ] or `fmap` forM subs (\path -> do- file <- fileExists path+ file <- TM.fileExists path if file then return True- else withDirectory path (existsAnycase $ AnchoredPath xs))+ else TM.withDirectory path (existsAnycase $ AnchoredPath xs)) -treeHas :: (Functor m, Monad m) => Tree m -> FilePath -> m Bool-treeHas tree path = fst `fmap` virtualTreeMonad (HS.exists $ floatPath path) tree+treeHas :: Monad m => Tree m -> FilePath -> m Bool+treeHas tree path = fst `fmap` TM.virtualTreeMonad (TM.exists $ floatPath path) tree -treeHasDir :: (Functor m, Monad m) => Tree m -> FilePath -> m Bool-treeHasDir tree path = fst `fmap` virtualTreeMonad (directoryExists $ floatPath path) tree+treeHasDir :: Monad m => Tree m -> FilePath -> m Bool+treeHasDir tree path = fst `fmap` TM.virtualTreeMonad (TM.directoryExists $ floatPath path) tree -treeHasFile :: (Functor m, Monad m) => Tree m -> FilePath -> m Bool-treeHasFile tree path = fst `fmap` virtualTreeMonad (fileExists $ floatPath path) tree+treeHasFile :: Monad m => Tree m -> FilePath -> m Bool+treeHasFile tree path = fst `fmap` TM.virtualTreeMonad (TM.fileExists $ floatPath path) tree
@@ -15,9 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}-- module Darcs.UI.Commands.WhatsNew ( whatsnew@@ -37,17 +34,16 @@ import Darcs.Patch ( PrimOf, PrimPatch, RepoPatch , applyToTree, plainSummaryPrims, primIsHunk- , listTouchedFiles, IsRepoType )-import Darcs.Patch.Apply ( ApplyState )-import Darcs.Patch.Choices ( patchChoicesLps, lpPatch )+import Darcs.Patch.Apply ( Apply, ApplyState )+import Darcs.Patch.Choices ( mkPatchChoices, labelPatches, unLabel )+import Darcs.Patch.Commute ( Commute ) import Darcs.Patch.FileHunk ( IsHunk (..) )-import Darcs.Patch.Format ( PatchListFormat (..) )+import Darcs.Patch.Format ( PatchListFormat ) import Darcs.Patch.Inspect ( PatchInspect (..) )-import Darcs.Patch.Patchy ( Patchy ) import Darcs.Patch.Permutations ( partitionRL ) import Darcs.Patch.Prim.Class ( PrimDetails (..) )-import Darcs.Patch.Show ( ShowPatch )+import Darcs.Patch.Show ( ShowPatch, ShowContextPatch ) import Darcs.Patch.Split ( primSplitter ) import Darcs.Patch.TouchesFiles ( choosePreTouching ) import Darcs.Patch.Witnesses.Ordered@@ -58,29 +54,29 @@ ( Sealed (..), Sealed2 (..) , unFreeLeft )-import Darcs.Patch.Witnesses.Unsafe ( unsafeCoerceP ) import Darcs.Patch.Witnesses.WZipper ( FZipper (..) ) import Darcs.Repository ( RepoJob (..), Repository- , listRegisteredFiles, readRecorded, readRepo- , unrecordedChangesWithPatches, withRepository+ , readRecorded+ , unrecordedChanges, withRepository ) import Darcs.Repository.Diff ( treeDiff ) import Darcs.Repository.Prefs ( filetypeFunction )-import Darcs.Repository.State ( getMovesPs, getReplaces ) import Darcs.UI.Commands ( DarcsCommand(..), withStdOpts, amInRepository , commandAlias, nodefaults )-import Darcs.Repository.Resolution ( patchsetConflictResolutions )+import Darcs.UI.Completion ( modifiedFileArgs ) import Darcs.UI.Commands.Util ( announceFiles, filterExistingPaths ) import Darcs.UI.Flags- ( DarcsFlag (Summary, LookForAdds, LookForMoves), diffAlgorithm, diffingOpts- , isUnified, useCache, fixSubPaths- , verbosity, isInteractive, isUnified, lookForAdds, lookForMoves, lookForReplaces, hasSummary- , scanKnown, useIndex+ ( DarcsFlag, diffAlgorithm+ , withContext, useCache, fixSubPaths+ , verbosity, isInteractive+ , lookForAdds, lookForMoves, lookForReplaces+ , scanKnown, useIndex, diffingOpts )-import Darcs.UI.Options ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags )+import Darcs.UI.Options+ ( DarcsOption, (^), odesc, ocheck, onormalise, defaultFlags, parseFlags, (?) ) import qualified Darcs.UI.Options.All as O import Darcs.UI.PrintPatch ( contextualPrintPatch, printPatch@@ -98,73 +94,37 @@ , skipOne, printSummary ) import qualified Darcs.UI.SelectChanges as S ( PatchSelectionOptions (..) )-import Darcs.Util.Path ( AbsolutePath, SubPath, toFilePath, fp2fn )+import Darcs.Util.Path ( AbsolutePath, SubPath, toFilePath ) import Darcs.Util.Printer- ( putDocLn, renderString, RenderMode(..)+ ( putDocLn, renderString , text, vcat ) import Darcs.Util.Prompt ( PromptConfig (..), promptChar ) --whatsnewBasicOpts :: DarcsOption a- (Maybe O.Summary- -> O.WithContext- -> Bool- -> O.LookFor- -> O.DiffAlgorithm- -> Maybe String- -> Maybe Bool- -> a)-whatsnewBasicOpts- = O.summary- ^ O.withContext- ^ O.machineReadable- ^ O.lookfor- ^ O.diffAlgorithm- ^ O.workingRepoDir- ^ O.interactive -- False--whatsnewAdvancedOpts :: DarcsOption a (O.UseIndex -> O.IncludeBoring -> a)-whatsnewAdvancedOpts = O.useIndex ^ O.includeBoring--whatsnewOpts :: DarcsOption a- (Maybe O.Summary- -> O.WithContext- -> Bool- -> O.LookFor- -> O.DiffAlgorithm- -> Maybe String- -> Maybe Bool- -> Maybe O.StdCmdAction- -> Bool- -> Bool- -> O.Verbosity- -> Bool- -> O.UseIndex- -> O.IncludeBoring- -> O.UseCache- -> Maybe String- -> Bool- -> Maybe String- -> Bool- -> a)-whatsnewOpts = whatsnewBasicOpts `withStdOpts` whatsnewAdvancedOpts+commonAdvancedOpts :: DarcsOption a (O.UseIndex -> O.IncludeBoring -> a)+commonAdvancedOpts = O.useIndex ^ O.includeBoring patchSelOpts :: [DarcsFlag] -> S.PatchSelectionOptions patchSelOpts flags = S.PatchSelectionOptions- { S.verbosity = verbosity flags+ { S.verbosity = verbosity ? flags , S.matchFlags = [] , S.interactive = isInteractive True flags , S.selectDeps = O.PromptDeps -- option not supported, use default- , S.summary = hasSummary (defaultSummary flags) flags- , S.withContext = isUnified flags+ , S.summary = getSummary flags+ , S.withContext = withContext ? flags } -defaultSummary :: [DarcsFlag] -> O.Summary-defaultSummary flags- | lookForAdds flags == O.YesLookForAdds = O.YesSummary- | parseFlags O.machineReadable flags = O.YesSummary- | otherwise = O.NoSummary+-- lookForAdds and machineReadable set YesSummary+-- unless NoSummary was given expressly+-- (or by default e.g. status)+getSummary :: [DarcsFlag] -> O.Summary+getSummary flags = case O.maybeSummary Nothing ? flags of+ Just O.NoSummary -> O.NoSummary+ Just O.YesSummary -> O.YesSummary+ Nothing+ | O.yes (lookForAdds flags) -> O.YesSummary+ | O.machineReadable ? flags -> O.YesSummary+ | otherwise -> O.NoSummary whatsnew :: DarcsCommand [DarcsFlag] whatsnew = DarcsCommand@@ -176,14 +136,24 @@ , commandExtraArgHelp = ["[FILE or DIRECTORY]..."] , commandCommand = whatsnewCmd , commandPrereq = amInRepository- , commandGetArgPossibilities = listRegisteredFiles+ , commandCompleteArgs = modifiedFileArgs , commandArgdefaults = nodefaults- , commandAdvancedOptions = odesc whatsnewAdvancedOpts+ , commandAdvancedOptions = odesc commonAdvancedOpts , commandBasicOptions = odesc whatsnewBasicOpts , commandDefaults = defaultFlags whatsnewOpts , commandCheckOptions = ocheck whatsnewOpts , commandParseOptions = onormalise whatsnewOpts }+ where+ whatsnewBasicOpts+ = O.maybeSummary Nothing+ ^ O.withContext+ ^ O.machineReadable+ ^ O.lookfor+ ^ O.diffAlgorithm+ ^ O.repoDir+ ^ O.interactive -- False+ whatsnewOpts = whatsnewBasicOpts `withStdOpts` commonAdvancedOpts whatsnewDescription :: String whatsnewDescription = "List unrecorded changes in the working tree."@@ -207,9 +177,9 @@ "* `a f` and `a d/` respectively mean a new, but unadded, file or\n" ++ " directory, when using `--look-for-adds`.\n" ++ "\n" ++- " An exclamation mark (!) as in `R! foo.c`, means the hunk is known to\n" ++- " conflict with a hunk in another patch. The phrase `duplicated`\n" ++- " means the hunk is known to be identical to a hunk in another patch.\n" +++ " An exclamation mark (!) as in `R! foo.c`, means the change is known to\n" +++ " conflict with a change in another patch. The phrase `duplicated`\n" +++ " means the change is known to be identical to a change in another patch.\n" ++ "\n" ++ "The `--machine-readable` option implies `--summary` while making it more\n" ++ "parsable. Modified files are only shown as `M f`, and moves are shown in\n" ++@@ -225,70 +195,96 @@ whatsnewCmd :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO () whatsnewCmd fps opts args =- withRepository (useCache opts) $ RepoJob $ \(repo :: Repository rt p wR wU wR) -> do- let scan = scanKnown (O.adds (parseFlags O.lookfor opts)) (parseFlags O.includeBoring opts)+ withRepository (useCache ? opts) $ RepoJob $ \(repo :: Repository rt p wR wU wR) -> do+ let scan = scanKnown (lookForAdds opts) (O.includeBoring ? opts) existing_files <- do files <- if null args then return Nothing else Just . nubSort <$> fixSubPaths fps args when (files == Just []) $ fail "No valid arguments were given."- files' <- traverse (filterExistingPaths repo (verbosity opts) (useIndex opts) scan) files+ files' <- traverse+ (filterExistingPaths+ repo (verbosity ? opts) (useIndex ? opts) scan (lookForMoves opts))+ files let files'' = fmap snd files' when (files'' == Just []) $ fail "None of the files you specified exist." return files''- let isLookForMoves = lookForMoves opts == O.YesLookForMoves && parseFlags O.summary opts /= Just O.NoSummary- isLookForAdds = lookForAdds opts == O.YesLookForAdds && parseFlags O.summary opts /= Just O.NoSummary- isLookForReplaces = lookForReplaces opts == O.YesLookForReplaces- isMachineReadable = parseFlags O.machineReadable opts- -- LookForAdds and LookForMoves implies Summary, unless it's explcitly disabled.- opts' | isLookForAdds = (Summary : filter (\o -> LookForAdds /= o &&- LookForMoves /= o ) opts)- | isMachineReadable = (Summary:opts)- | otherwise = opts- movesPs <- if isLookForMoves- then getMovesPs repo existing_files- else return NilFL- Sealed replacePs <- if isLookForReplaces- then getReplaces (diffingOpts opts) repo existing_files- else return (Sealed NilFL)- Sealed noLookChanges <- filteredUnrecordedChanges opts' repo existing_files movesPs- (unsafeCoerceP replacePs :: FL (PrimOf p) wR wR)++ -- get all unrecorded changes, possibly including unadded or even boring+ -- files if the appropriate options were supplied+ Sealed allInterestingChanges <-+ filteredUnrecordedChanges (diffingOpts opts)+ (lookForMoves opts) (lookForReplaces opts)+ repo existing_files++ -- get the recorded state pristine <- readRecorded repo- -- If we are looking for moves, return the corresponding FL of changes.- -- If we are looking for adds, return the corresponding FL of changes.- Sealed unaddedNewPathsPs <- if isLookForAdds++ -- the case --look-for-adds and --summary must be handled specially+ -- in order to distinguish added and unadded files++ -- TODO: it would be nice if we could return the pair+ -- (noLookChanges,unaddedNewPathsPs) in one go and also+ -- with proper witnesses (e.g. as noLookChanges +>+ unaddedNewPathsPs)+ -- This would also obviate the need for samePatchType.+ Sealed noLookChanges <-+ if haveLookForAddsAndSummary+ then+ -- do *not* look for adds here:+ filteredUnrecordedChanges (O.useIndex ? opts, O.ScanKnown, O.diffAlgorithm ? opts)+ (lookForMoves opts) (lookForReplaces opts)+ repo existing_files+ else return (Sealed NilFL)+ Sealed unaddedNewPathsPs <-+ if haveLookForAddsAndSummary then do- -- Use opts not opts', here, since we *do* want to look for adds.- Sealed lookChanges <- filteredUnrecordedChanges opts repo existing_files movesPs (unsafeCoerceP replacePs :: FL (PrimOf p) wR wR)- noLookAddsTree <- applyAddPatchesToPristine noLookChanges pristine- lookAddsTree <- applyAddPatchesToPristine lookChanges pristine- ftf <- filetypeFunction- -- Return the patches that create files/dirs that aren't yet added.- unFreeLeft <$> treeDiff (diffAlgorithm opts) ftf noLookAddsTree lookAddsTree+ noLookAddsTree <- applyAddPatchesToPristine noLookChanges pristine+ lookAddsTree <- applyAddPatchesToPristine allInterestingChanges pristine+ ftf <- filetypeFunction+ -- Return the patches that create files/dirs that aren't yet added.+ unFreeLeft <$> treeDiff (diffAlgorithm ? opts) ftf noLookAddsTree lookAddsTree else return (Sealed NilFL)- announceFiles (verbosity opts) existing_files "What's new in"- exitOnNoChanges (unaddedNewPathsPs, noLookChanges)+ -- avoid ambiguous typing for unaddedNewPathsPs:+ samePatchType noLookChanges unaddedNewPathsPs++ exitOnNoChanges allInterestingChanges+ announceFiles (verbosity ? opts) existing_files "What's new in" if maybeIsInteractive opts- then runInteractive (interactiveHunks pristine) opts' pristine noLookChanges- else do- printChanges repo opts' pristine noLookChanges- printUnaddedPaths unaddedNewPathsPs+ then+ runInteractive (interactiveHunks pristine) (patchSelOpts opts)+ (diffAlgorithm ? opts) pristine allInterestingChanges+ else+ if haveLookForAddsAndSummary+ then do+ printChanges pristine noLookChanges+ printUnaddedPaths unaddedNewPathsPs+ else do+ printChanges pristine allInterestingChanges where- -- |Filter out hunk patches (leaving add patches) and return the tree+ haveSummary = O.yes (getSummary opts)+ haveLookForAddsAndSummary = haveSummary && O.yes (lookForAdds opts)++ -- Filter out hunk patches (leaving add patches) and return the tree -- resulting from applying the filtered patches to the pristine tree. applyAddPatchesToPristine ps pristine = do adds :> _ <- return $ partitionRL primIsHunk $ reverseFL ps applyToTree (reverseRL adds) pristine - exitOnNoChanges :: (FL p wX wY, FL p wU wV) -> IO ()- exitOnNoChanges (NilFL, NilFL) = do putStrLn "No changes!"- exitWith $ ExitFailure 1+ exitOnNoChanges :: FL p wX wY -> IO ()+ exitOnNoChanges NilFL = do putStrLn "No changes!"+ exitWith $ ExitFailure 1 exitOnNoChanges _ = return () + -- This function does nothing. Its purpose is to enforce the+ -- same patch type for the two passed FLs. This is necessary+ -- in order to avoid ambiguous typing for unaddedNewPathsPs.+ samePatchType :: FL p wX wY -> FL p wU wV -> IO ()+ samePatchType _ _ = return ()+ printUnaddedPaths :: PrimPatch p => FL p wX wY -> IO () printUnaddedPaths NilFL = return () printUnaddedPaths ps =- putDocLn . lowercaseAs . renderString Encode . (plainSummaryPrims False []) $ ps+ putDocLn . lowercaseAs . renderString . (plainSummaryPrims False) $ ps -- Make any add markers lowercase, to distinguish new-but-unadded files -- from those that are unrecorded, but added.@@ -296,56 +292,55 @@ lowercaseA ('A' : x) = 'a' : x lowercaseA x = x - -- |Appropriately print changes, according to the passed flags.- 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+ -- Appropriately print changes, according to the passed flags.+ -- Note this cannot make distinction between unadded and added files.+ printChanges :: ( IsHunk p, ShowPatch p, ShowContextPatch p+ , PatchListFormat p, Apply p+ , PrimDetails p, ApplyState p ~ Tree)+ => Tree IO -> FL p wX wY -> IO ()- 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+ printChanges pristine changes+ | haveSummary = putDocLn $ plainSummaryPrims machineReadable changes+ | O.yes (withContext ? opts) = 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 rt p wR wU wT. (RepoPatch p, ApplyState p ~ Tree,- ApplyState (PrimOf p) ~ Tree)- => [DarcsFlag]+ -- return the unrecorded changes that affect an optional list of paths.+ filteredUnrecordedChanges :: forall rt p wR wU wT. (RepoPatch p, ApplyState p ~ Tree)+ => (O.UseIndex, O.ScanKnown, O.DiffAlgorithm)+ -> O.LookForMoves+ -> O.LookForReplaces -> 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 =+ filteredUnrecordedChanges diffing lfm lfr repo files = let filePaths = map toFilePath <$> files in- choosePreTouching filePaths <$> unrecordedChangesWithPatches movesPs replacesPs (diffingOpts opts') repo files+ choosePreTouching filePaths <$>+ unrecordedChanges diffing lfm lfr repo files -- | Runs the 'InteractiveSelectionM' code-runInteractive :: (PatchListFormat p, IsHunk p, Patchy p, ShowPatch p,- PrimPatch p, PatchInspect p, PrimDetails p,- ApplyState p ~ Tree)+runInteractive :: PrimPatch p => InteractiveSelectionM p wX wY () -- Selection to run- -> [DarcsFlag] -- Command-line options+ -> S.PatchSelectionOptions+ -> O.DiffAlgorithm -> Tree IO -- Pristine -> FL p wX wY -- A list of patches -> IO ()-runInteractive i opts pristine ps' = do- let (choices',lps') = patchChoicesLps ps'- let ps = evalStateT i $+runInteractive i patchsel diffalg pristine ps' = do+ let lps' = labelPatches Nothing ps'+ choices' = mkPatchChoices lps'+ ps = evalStateT i $ ISC { total = lengthFL lps' , current = 0 , lps = FZipper NilRL lps' , choices = choices' } void $ runReaderT ps $- selectionContextPrim First "view" (patchSelOpts opts)- (Just (primSplitter (diffAlgorithm opts)))+ selectionContextPrim First "view" patchsel+ (Just (primSplitter diffalg)) Nothing (Just pristine) -- | The interactive part of @darcs whatsnew@-interactiveHunks :: (PatchListFormat p, IsHunk p, Patchy p, ShowPatch p,+interactiveHunks :: (IsHunk p, ShowPatch p, ShowContextPatch p, Commute p, PatchInspect p, PrimDetails p, ApplyState p ~ Tree) => Tree IO -> InteractiveSelectionM p wX wY () interactiveHunks pristine = do@@ -353,7 +348,7 @@ case c of Nothing -> liftIO $ putStrLn "No more changes!" Just (Sealed2 lp) -> do- liftIO $ printPatch (lpPatch lp)+ liftIO $ printPatch (unLabel lp) repeatThis lp where repeatThis lp = do@@ -362,14 +357,14 @@ (PromptConfig thePrompt (keysFor basic_options) (keysFor adv_options) (Just 'n') "?h") case yorn of- -- View hunk in context- 'v' -> liftIO (contextualPrintPatch pristine (lpPatch lp))+ -- View change in context+ 'v' -> liftIO (contextualPrintPatch pristine (unLabel lp)) >> repeatThis lp -- View summary of the change- 'x' -> liftIO (printSummary (lpPatch lp))+ 'x' -> liftIO (printSummary (unLabel lp)) >> repeatThis lp- -- View hunk and move on- 'y' -> liftIO (contextualPrintPatch pristine (lpPatch lp))+ -- View change and move on+ 'y' -> liftIO (contextualPrintPatch pristine (unLabel lp)) >> decide True lp >> next_hunk -- Go to the next patch 'n' -> decide False lp >> next_hunk@@ -379,12 +374,12 @@ (return ()) (\f -> decideWholeFile f False) next_hunk- -- View hunk in a pager- 'p' -> liftIO (printPatchPager $ lpPatch lp)+ -- View change in a pager+ 'p' -> liftIO (printPatchPager $ unLabel lp) >> repeatThis lp- -- Next hunk+ -- Next change 'j' -> next_hunk- -- Previous hunk+ -- Previous change 'k' -> prev_hunk -- Start from the first change 'g' -> start_over@@ -397,34 +392,49 @@ next_hunk = skipOne >> skipMundane >> interactiveHunks pristine prev_hunk = backOne >> interactiveHunks pristine options_yn =- [ KeyPress 'v' "view this hunk in a context"- , KeyPress 'y'- "view this hunk in a context and go to the next one"- , KeyPress 'n' "go to the next hunk" ]+ [ KeyPress 'v' "view this change in a context"+ , KeyPress 'y' "view this change in a context and go to the next one"+ , KeyPress 'n' "skip this change and its dependencies" ] optionsView =- [ KeyPress 'p' "view this hunk in context wih pager "- , KeyPress 'x' "view a summary of this patch"+ [ KeyPress 'p' "view this change in context wih pager "+ , KeyPress 'x' "view a summary of this change" ] optionsNav = [ KeyPress 'q' "quit whatsnew" , KeyPress 's' "skip the rest of the changes to this file"- , KeyPress 'j' "skip to the next hunk"- , KeyPress 'k' "back up to previous hunk"- , KeyPress 'g' "start over from the first hunk"+ , KeyPress 'j' "go to the next change"+ , KeyPress 'k' "back up to previous change"+ , KeyPress 'g' "start over from the first change" ] basic_options = [ options_yn ] adv_options = [ optionsView, optionsNav ] --- |status is an alias for whatsnew, with implicit Summary and LookForAdds++-- | status is an alias for whatsnew, with implicit Summary and LookForAdds -- flags. We override the default description, to include the implicit flags. status :: DarcsCommand [DarcsFlag]-status = statusAlias { commandCommand = statusCmd- , commandDescription = statusDesc- }+status = statusAlias+ { commandDescription = statusDesc+ , commandAdvancedOptions = odesc commonAdvancedOpts+ , commandBasicOptions = odesc statusBasicOpts+ , commandDefaults = defaultFlags statusOpts+ , commandCheckOptions = ocheck statusOpts+ , commandParseOptions = onormalise statusOpts+ } where statusAlias = commandAlias "status" Nothing whatsnew- statusCmd fps fs = commandCommand whatsnew fps (Summary : LookForAdds : fs) statusDesc = "Alias for `darcs " ++ commandName whatsnew ++ " -ls '."+ statusBasicOpts+ = O.maybeSummary (Just O.YesSummary)+ ^ O.withContext+ ^ O.machineReadable+ ^ O.lookforadds O.YesLookForAdds+ ^ O.lookforreplaces+ ^ O.lookformoves+ ^ O.diffAlgorithm+ ^ O.repoDir+ ^ O.interactive+ statusOpts = statusBasicOpts `withStdOpts` commonAdvancedOpts maybeIsInteractive :: [DarcsFlag] -> Bool maybeIsInteractive = maybe False id . parseFlags O.interactive
@@ -15,9 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}-- module Darcs.UI.CommandsAux ( checkPaths , maliciousPatches
@@ -0,0 +1,185 @@+-- | How to complete arguments+{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}+module Darcs.UI.Completion+ ( fileArgs, knownFileArgs, unknownFileArgs, modifiedFileArgs+ , noArgs, prefArgs+ ) where++import Prelude ()+import Darcs.Prelude++import Data.List ( (\\), stripPrefix )+import Data.List.Ordered ( nubSort, minus )+import Data.Maybe ( mapMaybe )++import Darcs.Patch ( listTouchedFiles )++import Darcs.Repository.Flags+ ( UseCache(..)+ )+import Darcs.Repository.Prefs+ ( getPreflist+ )+import Darcs.Repository.Job+ ( RepoJob(..)+ , withRepository+ )+import Darcs.Repository.State+ ( readRecordedAndPending+ , readUnrecordedFiltered+ , unrecordedChanges+ , restrictDarcsdir+ , applyTreeFilter+ , TreeFilter(..)+ )++import Darcs.UI.Flags ( DarcsFlag )+import qualified Darcs.UI.Flags as Flags+import qualified Darcs.UI.Options.All as O++import Darcs.Util.File+ ( doesDirectoryReallyExist+ )+import Darcs.Util.Global+ ( darcsdir+ )+import Darcs.Util.Path+ ( AnchoredPath, anchorPath+ , AbsolutePath, toPath, floatSubPath, makeSubPathOf+ )+import Darcs.Util.Tree as Tree+ ( Tree, ItemType(..)+ , expand, expandPath, list, findTree, itemType, emptyTree+ )+import Darcs.Util.Tree.Plain ( readPlainTree )++-- | Return all files available under the original working+-- directory regardless of their repo state.+-- Subdirectories get a separator (slash) appended.+fileArgs :: (AbsolutePath, AbsolutePath)+ -> [DarcsFlag]+ -> [String]+ -> IO [FilePath]+fileArgs (_, orig) _flags args =+ notYetListed args $+ fmap (map anchoredToFilePath . listItems) $+ Tree.expand . applyTreeFilter restrictDarcsdir =<< readPlainTree (toPath orig)++-- | Return all files available under the original working directory that+-- are unknown to darcs but could be added.+-- Subdirectories get a separator (slash) appended.+unknownFileArgs :: (AbsolutePath, AbsolutePath)+ -> [DarcsFlag]+ -> [String]+ -> IO [FilePath]+unknownFileArgs fps flags args = notYetListed args $ do+ let sk = if Flags.includeBoring flags then O.ScanBoring else O.ScanAll+ lfm = Flags.lookForMoves flags+ lfr = Flags.lookForReplaces flags+ RepoTrees {have, known} <- repoTrees O.UseIndex sk lfm lfr+ known_paths <- listHere known fps+ have_paths <- listHere have fps+ return $ map anchoredToFilePath $ nubSort have_paths `minus` nubSort known_paths++-- | Return all files available under the original working directory that+-- are known to darcs (either recorded or pending).+-- Subdirectories get a separator (slash) appended.+knownFileArgs :: (AbsolutePath, AbsolutePath)+ -> [DarcsFlag]+ -> [String]+ -> IO [FilePath]+knownFileArgs fps flags args = notYetListed args $ do+ let (ui, sk, _) = Flags.diffingOpts flags+ lfm = Flags.lookForMoves flags+ lfr = Flags.lookForReplaces flags+ RepoTrees {known} <- repoTrees ui sk lfm lfr+ map anchoredToFilePath <$> listHere known fps++-- | Return all files available under the original working directory that+-- are modified (relative to the recorded state).+-- Subdirectories get a separator (slash) appended.+modifiedFileArgs :: (AbsolutePath, AbsolutePath)+ -> [DarcsFlag]+ -> [String]+ -> IO [FilePath]+modifiedFileArgs fps flags args = notYetListed args $ do+ let (ui, sk, _) = Flags.diffingOpts flags+ lfm = Flags.lookForMoves flags+ lfr = Flags.lookForReplaces flags+ RepoTrees {new} <- repoTrees ui sk lfm lfr+ case uncurry makeSubPathOf fps of+ Nothing -> return []+ Just here ->+ return $ mapMaybe (stripPathPrefix (toPath here) . drop 2) new++-- | Return the available prefs of the given kind.+prefArgs :: String+ -> (AbsolutePath, AbsolutePath)+ -> [DarcsFlag]+ -> [String]+ -> IO [String]+prefArgs name _ _ _ = getPreflist name++-- | Return an empty list.+noArgs :: (AbsolutePath, AbsolutePath) -> [DarcsFlag] -> [String] -> IO [String]+noArgs _ _ _ = return []++-- * unexported helper functions++data RepoTrees m = RepoTrees+ { have :: Tree m -- ^ working tree+ , known :: Tree m -- ^ recorded and pending+ , new :: [FilePath] -- ^ unrecorded paths+ }++repoTrees :: O.UseIndex -> O.ScanKnown -> O.LookForMoves -> O.LookForReplaces+ -> IO (RepoTrees IO)+repoTrees ui sk lfm lfr = do+ inDarcsRepo <- doesDirectoryReallyExist darcsdir+ if inDarcsRepo then+ withRepository NoUseCache $ RepoJob $ \r -> do+ known <- readRecordedAndPending r+ have <- readUnrecordedFiltered r ui sk lfm Nothing+ -- we are only interested in the affected paths so the diff+ -- algorithm is irrelevant+ new <- listTouchedFiles <$> unrecordedChanges (ui, sk, O.MyersDiff) lfm lfr r Nothing+ return $ RepoTrees {..}+ else+ return RepoTrees {have = emptyTree, known = emptyTree, new = []}++-- this is for completion which should give us everything under the original wd+subtreeHere :: Tree IO -> (AbsolutePath, AbsolutePath) -> IO (Maybe (Tree IO))+subtreeHere tree fps =+ case floatSubPath <$> uncurry makeSubPathOf fps of+ Nothing -> do+ return Nothing -- here is no subtree of the repo+ Just here -> do+ flip findTree here <$> expandPath tree here++listHere :: Tree IO+ -> (AbsolutePath, AbsolutePath)+ -> IO [(AnchoredPath, ItemType)]+listHere tree fps = do+ msubtree <- subtreeHere tree fps+ case msubtree of+ Nothing -> return []+ Just subtree -> listItems <$> expand subtree++listItems :: Tree m -> [(AnchoredPath, ItemType)]+listItems = map (\(p, i) -> (p, itemType i)) . Tree.list++anchoredToFilePath :: (AnchoredPath, ItemType) -> [Char]+anchoredToFilePath (path, TreeType) = anchorPath "" path -- ++ "/"+anchoredToFilePath (path, BlobType) = anchorPath "" path++stripPathPrefix :: FilePath -> FilePath -> Maybe FilePath+stripPathPrefix = stripPrefix . addSlash where+ addSlash [] = []+ addSlash xs = xs ++ "/"++-- | Turn an action that creates all possible completions into one+-- that removes already given arguments.+notYetListed :: [String] -> IO [String] -> IO [String]+notYetListed already complete = do+ possible <- complete+ return $ possible \\ already
@@ -6,7 +6,7 @@ import Control.Monad.Writer import Data.Char ( isSpace ) import Data.Functor.Compose ( Compose(..) )-import Data.List ( nub, intercalate )+import Data.List ( nub ) import Data.Maybe ( catMaybes ) import qualified Data.Map as M import System.Console.GetOpt@@ -85,11 +85,9 @@ showCmdName (NormalCmd name) = name runChecks :: String -> ([DarcsFlag] -> [String]) -> [DarcsFlag] -> Writer [String] [DarcsFlag]-runChecks source check fs = case check fs of- [] -> return fs- es -> do- tell [intercalate "\n" $ map ((source++": ")++) es]- return fs+runChecks source check fs = do+ tell $ map ((source++": ")++) $ check fs+ return fs -- | Parse a list of lines from a defaults file, returning a list of 'DarcsFlag', -- given the current working directory, the command name, and a list of 'DarcsOption'
@@ -1,8 +1,9 @@-{-# LANGUAGE CPP #-} module Darcs.UI.Email ( makeEmail , readEmail , formatHeader+ -- just for testing+ , prop_qp_roundtrip ) where import Prelude ()@@ -11,14 +12,12 @@ import Data.Char ( digitToInt, isHexDigit, ord, intToDigit, isPrint, toUpper ) import Data.List ( isInfixOf ) import Darcs.Util.Printer- ( Doc, ($$), (<+>), (<>), text, empty, packedString, renderPS- , RenderMode(..)- )+ ( Doc, ($$), (<+>), (<>), text, empty, packedString, renderPS ) import Darcs.Util.ByteString ( packStringToUTF8, dropSpace, linesPS, betweenLinesPS ) import qualified Data.ByteString as B (ByteString, length, null, tail ,drop, head, concat, singleton- ,pack, append, empty, unpack+ ,pack, append, empty, unpack, snoc ) import qualified Data.ByteString.Char8 as BC (index, head, pack) import Data.ByteString.Internal as B (c2w, createAndTrim)@@ -131,7 +130,8 @@ word8ToUDigit :: Word8 -> Char word8ToUDigit = toUpper . intToDigit . fromIntegral --- TODO is this doing mime encoding??+-- Encode a ByteString according to "Quoted Printable" defined by MIME+-- (https://tools.ietf.org/html/rfc2045#section-6.7) qpencode :: B.ByteString -> B.ByteString qpencode s = unsafePerformIO -- Really only (3 + 2/75) * length or something in the worst case@@ -143,7 +143,7 @@ c | c == newline -> do poke (buf `plusPtr` bufi) newline encode ps' qlineMax buf (bufi+1)- | n == 0 && B.length ps > 1 ->+ | n == 0 && B.length ps >= 1 -> do poke (buf `plusPtr` bufi) equals poke (buf `plusPtr` (bufi+1)) newline encode ps qlineMax buf (bufi + 2)@@ -220,7 +220,7 @@ fromMaybe "x-unknown" mcharset ++ "\"") $$ text "Content-Transfer-Encoding: quoted-printable" $$ text ""- $$ packedString (qpencode (renderPS Standard contents))+ $$ packedString (qpencode (renderPS contents)) $$ text "" $$ text "--=_" Nothing -> empty)@@ -229,10 +229,10 @@ $$ text "Content-Transfer-Encoding: quoted-printable" $$ text "Content-Description: Patch preview" $$ text ""- $$ (case betweenLinesPS (BC.pack "New patches:") (BC.pack "Context:") (renderPS Standard bundle) of+ $$ (case betweenLinesPS (BC.pack "New patches:") (BC.pack "Context:") (renderPS bundle) of Just s -> packedString $ qpencode s -- this should not happen, but in case it does, keep everything- Nothing -> packedString $ qpencode $ renderPS Standard bundle)+ Nothing -> packedString $ qpencode $ renderPS bundle) $$ text "--=_" $$ text "Content-Type: application/x-darcs-patch" <> (case mfilename of@@ -242,7 +242,7 @@ $$ text "Content-Disposition: attachment" $$ text "Content-Description: A darcs patch for your repository!" $$ text ""- $$ packedString (qpencode (renderPS Standard bundle))+ $$ packedString (qpencode (renderPS bundle)) $$ text "--=_--" $$ text "" $$ text "."@@ -257,3 +257,6 @@ Nothing -> s -- if it wasn't an email in the first place, just pass along. Just s' -> qpdecode s' +-- note: qpdecode appends an extra '\n'+prop_qp_roundtrip :: B.ByteString -> Bool+prop_qp_roundtrip s = B.snoc s 10 == (qpdecode . qpencode) s
@@ -28,6 +28,7 @@ import Prelude () import Darcs.Prelude+import Darcs.Util.Text ( showCommandLine ) import Data.Maybe ( isJust, isNothing, maybeToList ) import Control.Monad ( unless, when, filterM, liftM2, void )@@ -35,11 +36,7 @@ import System.Exit ( ExitCode(..) ) import System.Environment ( getEnv-#if __GLASGOW_HASKELL__ >= 706 , getExecutablePath-#else- , getProgName-#endif ) import System.IO ( hPutStr, hPutStrLn, hClose, hIsTerminalDevice, stdout, stderr, Handle )@@ -49,7 +46,10 @@ import System.Process ( createProcess, proc, CreateProcess(..), runInteractiveProcess, waitForProcess, StdStream(..) ) import System.Process.Internals ( ProcessHandle ) -import GHC.IO.Encoding ( setFileSystemEncoding, setForeignEncoding, char8 )+import GHC.IO.Encoding+ ( getFileSystemEncoding+ , setForeignEncoding+ , setLocaleEncoding ) import Foreign.C.String ( CString, peekCString ) @@ -95,7 +95,7 @@ import Darcs.Util.Printer ( Doc, Printers, hPutDocLnWith, hPutDoc, hPutDocLn, hPutDocWith, ($$), renderPS, simplePrinters, hPutDocCompr,- text, empty, packedString, vcat, renderString, RenderMode(..) )+ text, empty, packedString, vcat, renderString ) import qualified Darcs.Util.Ratified as Ratified import Darcs.UI.Email ( formatHeader ) @@ -116,28 +116,24 @@ -- |Get the name of the darcs executable (as supplied by @getExecutablePath@) darcsProgram :: IO String-#if __GLASGOW_HASKELL__ >= 706 darcsProgram = getExecutablePath-#else-darcsProgram = getProgName-#endif -pipeDoc :: RenderMode -> String -> [String] -> Doc -> IO ExitCode+pipeDoc :: String -> [String] -> Doc -> IO ExitCode pipeDoc = pipeDocInternal (PipeToOther simplePrinters) data WhereToPipe = PipeToSsh Compression -- ^ if pipe to ssh, can choose to compress or not | PipeToOther Printers -- ^ otherwise, can specify printers -pipeDocInternal :: WhereToPipe -> RenderMode -> String -> [String] -> Doc -> IO ExitCode-pipeDocInternal whereToPipe target c args inp = withoutNonBlock $ withoutProgress $- do debugMessage $ unwords (c:args)+pipeDocInternal :: WhereToPipe -> String -> [String] -> Doc -> IO ExitCode+pipeDocInternal whereToPipe c args inp = withoutNonBlock $ withoutProgress $+ do debugMessage $ "Exec: " ++ showCommandLine (c:args) (Just i,_,_,pid) <- createProcess (proc c args){ std_in = CreatePipe- {- , delegate_ctlc = True -- requires process 1.2.2.0 -} }+ , delegate_ctlc = True} debugMessage "Start transferring data" case whereToPipe of- PipeToSsh GzipCompression -> hPutDocCompr target i inp- PipeToSsh NoCompression -> hPutDoc target i inp- PipeToOther printers -> hPutDocWith printers target i inp+ PipeToSsh GzipCompression -> hPutDocCompr i inp+ PipeToSsh NoCompression -> hPutDoc i inp+ PipeToOther printers -> hPutDocWith printers i inp hClose i rval <- waitForProcess pid debugMessage "Finished transferring data"@@ -145,10 +141,10 @@ putStrLn $ "Command not found:\n "++ show (c:args) return rval -pipeDocSSH :: Compression -> RenderMode -> SshFilePath -> [String] -> Doc -> IO ExitCode-pipeDocSSH compress target remoteAddr args input = do+pipeDocSSH :: Compression -> SshFilePath -> [String] -> Doc -> IO ExitCode+pipeDocSSH compress remoteAddr args input = do (ssh, ssh_args) <- getSSH SSH- pipeDocInternal (PipeToSsh compress) target ssh (ssh_args++ (sshUhost remoteAddr:args)) input+ pipeDocInternal (PipeToSsh compress) ssh (ssh_args ++ ("--":sshUhost remoteAddr:args)) input sendEmail :: String -> String -> String -> String -> String -> String -> IO () sendEmail f t s cc scmd body =@@ -169,7 +165,7 @@ putHeader "Subject" s unless (null cc) $ putHeader "Cc" cc putHeader "X-Mail-Originator" "Darcs Version Control System"- hPutDocLn Standard h body+ hPutDocLn h body where putHeader field value = B.hPut h (B.append (formatHeader field value) newline) newline = B.singleton 10@@ -200,10 +196,10 @@ withOpenTemp $ \(hat,at) -> do ftable' <- case mbundle of Just (content,bundle) -> do- hPutDocLn Standard hat bundle- return [ ('b', renderString Standard content) , ('a', at) ]+ hPutDocLn hat bundle+ return [ ('b', renderString content) , ('a', at) ] Nothing ->- return [ ('b', renderString Standard body) ]+ return [ ('b', renderString body) ] hClose hat let ftable = [ ('t',addressOnly t),('c',cc),('f',f),('s',s) ] ++ ftable' r <- execSendmail ftable scmd fn@@ -217,7 +213,7 @@ withCString cc $ \ccp -> withCString s $ \sp -> withOpenTemp $ \(h,fn) -> do- hPutDoc Standard h body+ hPutDoc h body hClose h writeDocBinFile "mailed_patch" body cfn <- canonFilename fn@@ -245,7 +241,7 @@ hPutStrLn h $ "To: "++ t hPutStrLn h $ find_from (linesPS body) hPutStrLn h $ find_subject (linesPS body)- hPutDocLn Standard h $ fixit $ linesPS body+ hPutDocLn h $ fixit $ linesPS body hClose h let ftable = [('t',t)] r <- execSendmail ftable scmd fn@@ -299,15 +295,15 @@ #endif execPSPipe :: String -> [String] -> B.ByteString -> IO B.ByteString-execPSPipe c args ps = fmap (renderPS Standard)- $ execDocPipe Standard c args+execPSPipe c args ps = fmap renderPS+ $ execDocPipe c args $ packedString ps -execAndGetOutput :: RenderMode -> FilePath -> [String] -> Doc+execAndGetOutput :: FilePath -> [String] -> Doc -> IO (ProcessHandle, MVar (), B.ByteString)-execAndGetOutput target c args instr = do+execAndGetOutput c args instr = do (i,o,e,pid) <- runInteractiveProcess c args Nothing Nothing- _ <- forkIO $ hPutDoc target i instr >> hClose i+ _ <- forkIO $ hPutDoc i instr >> hClose i mvare <- newEmptyMVar _ <- forkIO ((Ratified.hGetContents e >>= -- ratify: immediately consumed hPutStr stderr)@@ -315,9 +311,9 @@ out <- B.hGetContents o return (pid, mvare, out) -execDocPipe :: RenderMode -> String -> [String] -> Doc -> IO Doc-execDocPipe target c args instr = withoutProgress $ do- (pid, mvare, out) <- execAndGetOutput target c args instr+execDocPipe :: String -> [String] -> Doc -> IO Doc+execDocPipe c args instr = withoutProgress $ do+ (pid, mvare, out) <- execAndGetOutput c args instr rval <- waitForProcess pid takeMVar mvare case rval of@@ -327,9 +323,9 @@ -- The following is needed for diff, which returns non-zero whenever -- the files differ.-execPipeIgnoreError :: RenderMode -> String -> [String] -> Doc -> IO Doc-execPipeIgnoreError target c args instr = withoutProgress $ do- (pid, mvare, out) <- execAndGetOutput target c args instr+execPipeIgnoreError :: String -> [String] -> Doc -> IO Doc+execPipeIgnoreError c args instr = withoutProgress $ do+ (pid, mvare, out) <- execAndGetOutput c args instr _ <- waitForProcess pid takeMVar mvare return $ if B.null out then empty else packedString out@@ -341,7 +337,7 @@ signString (SignSSL idf) d = signSSL idf d signPGP :: [String] -> Doc -> IO Doc-signPGP args = execDocPipe Standard "gpg" ("--clearsign":args)+signPGP args = execDocPipe "gpg" ("--clearsign":args) signSSL :: String -> Doc -> IO Doc signSSL idfile t =@@ -356,7 +352,7 @@ >>= B.writeFile cert opensslDoc ["smime", "-sign", "-signer", cert, "-inkey", idfile, "-noattr", "-text"] t- where opensslDoc = execDocPipe Standard "openssl"+ where opensslDoc = execDocPipe "openssl" opensslPS = execPSPipe "openssl" @@ -418,27 +414,27 @@ where opensslPS = execPSPipe "openssl" viewDoc :: Doc -> IO ()-viewDoc = viewDocWith simplePrinters Encode+viewDoc = viewDocWith simplePrinters -viewDocWith :: Printers -> RenderMode -> Doc -> IO ()-viewDocWith pr mode msg = do+viewDocWith :: Printers -> Doc -> IO ()+viewDocWith pr msg = do isTerminal <- hIsTerminalDevice stdout- void $ if isTerminal && lengthGreaterThan (20 :: Int) (lines $ renderString mode msg)+ void $ if isTerminal && lengthGreaterThan (20 :: Int) (lines $ renderString msg) then do mbViewerPlusArgs <- getViewer case mbViewerPlusArgs of Just viewerPlusArgs -> do let (viewer : args) = words viewerPlusArgs- pipeDocToPager viewer args pr mode msg+ pipeDocToPager viewer args pr msg Nothing -> return $ ExitFailure 127 -- No such command -- TEMPORARY passing the -K option should be removed as soon as -- we can use the delegate_ctrl_c feature in process- `ortryrunning` pipeDocToPager "less" ["-RK"] pr mode msg- `ortryrunning` pipeDocToPager "more" [] pr mode msg+ `ortryrunning` pipeDocToPager "less" ["-RK"] pr msg+ `ortryrunning` pipeDocToPager "more" [] pr msg #ifdef WIN32- `ortryrunning` pipeDocToPager "more.com" [] pr mode msg+ `ortryrunning` pipeDocToPager "more.com" [] pr msg #endif- `ortryrunning` pipeDocToPager "" [] pr mode msg- else pipeDocToPager "" [] pr mode msg+ `ortryrunning` pipeDocToPager "" [] pr msg+ else pipeDocToPager "" [] pr msg where lengthGreaterThan n _ | n <= 0 = True lengthGreaterThan _ [] = False lengthGreaterThan n (_:xs) = lengthGreaterThan (n-1) xs@@ -448,13 +444,13 @@ `catchall` return Nothing -pipeDocToPager :: String -> [String] -> Printers -> RenderMode -> Doc -> IO ExitCode+pipeDocToPager :: String -> [String] -> Printers -> Doc -> IO ExitCode -pipeDocToPager "" _ pr mode inp = do- hPutDocLnWith pr mode stdout inp+pipeDocToPager "" _ pr inp = do+ hPutDocLnWith pr stdout inp return ExitSuccess -pipeDocToPager c args pr mode inp = pipeDocInternal (PipeToOther pr) mode c args inp+pipeDocToPager c args pr inp = pipeDocInternal (PipeToOther pr) c args inp -- | Given two shell commands as arguments, execute the former. The -- latter is then executed if the former failed because the executable@@ -522,7 +518,6 @@ getEditor :: IO String getEditor = getEnv "DARCS_EDITOR" `catchall`- getEnv "DARCSEDITOR" `catchall` getEnv "VISUAL" `catchall` getEnv "EDITOR" `catchall` return "nano" @@ -532,37 +527,45 @@ a `catchall` b = a `catchNonSignal` (\_ -> b) --- | In some environments, darcs requires that certain global GHC library variables that--- control the encoding used in internal translations are set to specific values.+-- | On Posix systems, GHC by default uses the user's locale encoding to+-- determine how to decode/encode the raw byte sequences in the Posix API+-- to/from 'String'. It also uses certain special variants of this+-- encoding to determine how to handle encoding errors. ----- @setDarcsEncoding@ enforces those settings, and should be called before the--- first time any darcs operation is run, and again if anything else might have--- set those encodings to different values.+-- See "GHC.IO.Encoding" for details. ----- Note that it isn't thread-safe and has a global effect on your program.+-- In particular, the default variant used for command line arguments and+-- environment variables is //ROUNDTRIP, which means that /any/ byte sequence+-- can be decoded and re-encoded w/o failure or loss of information. To+-- enable this, GHC uses code points that are outside the range of the regular+-- unicode set. This is what you get with 'getFileSystemEncoding'. ----- The current behaviour of this function is as follows, though this may--- change in future:+-- We need to preserve the raw bytes e.g. for file names passed in by the+-- user and also when reading file names from disk; also when re-generating+-- files from patches, and when we display them to the user. ----- Encodings are only set on GHC 7.4 and up, on any non-Windows platform.+-- So we want to use this encoding variant for *all* IO and for (almost) all+-- conversions between raw bytes and 'String's. The encoding used for IO from+-- and to handles is controlled by 'setLocaleEncoding' which we use here to+-- make it equal to the //ROUNDTRIP variant. ----- Two encodings are set, both to @GHC.IO.Encoding.char8@:--- @GHC.IO.Encoding.setFileSystemEncoding@ and @GHC.IO.Encoding.setForeignEncoding@.+-- @setDarcsEncoding@ should be called before the+-- first time any darcs operation is run, and again if anything else might have+-- set those encodings to different values. ----- Prevent HLint from warning us about a redundant do if the macro isn't--- defined:+-- Note that it isn't thread-safe and has a global effect on your program.+--+-- On Windows, this function does (and should) not do anything. setDarcsEncodings :: IO ()+#ifdef WIN32+setDarcsEncodings = return ()+#else setDarcsEncodings = do---- This is needed for appropriate behaviour from getArgs and from general--- filesystem calls (e.g. getDirectoryContents, readFile, ...)- setFileSystemEncoding char8---- This ensures that foreign calls made by hashed-storage to stat--- filenames returned from getDirectoryContents are translated appropriately- setForeignEncoding char8-- return ()+ e <- getFileSystemEncoding+ -- TODO check if we have to set this, too.+ setForeignEncoding e+ setLocaleEncoding e+#endif -- The following functions are copied from the encoding package (BSD3 -- licence, by Henning Günther).
@@ -17,59 +17,35 @@ {-# 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- -- using helpers from this module instead.- F.DarcsFlag( .. )- , compression+ ( F.DarcsFlag+ -- FIXME these are temporary exceptions+ ( WorkRepoDir -- init+ , NewRepo -- convert, clone+ , UpToPattern -- clone --to-xxx -> -xxx hack+ , UpToPatch -- same+ , UpToHash -- same+ , OnePattern -- same+ , OnePatch -- same+ , OneHash -- same+ ) , remoteDarcs- , reorder- , minimize- , editDescription , diffingOpts , diffOpts , scanKnown- , externalMerge , wantGuiPause , isInteractive- , maxCount , willRemoveLogFile- , isUnified- , doHappyForwarding , includeBoring- , doAllowCaseOnly- , doAllowWindowsReserved- , doReverse- , usePacks- , showChangesOnlyToFiles- , removeFromAmended- , toMatchFlags- , verbosity- , useCache- , umask- , dryRun , lookForAdds , lookForMoves , lookForReplaces- , diffAlgorithm- , runTest- , testChanges- , setScriptsExecutable- , withWorkingDir- , leaveTestDir- , remoteRepos , setDefault- , cloneKind- , workRepo , allowConflicts- , runPatchIndex- , useIndex- , hasSummary , hasXmlOutput- , selectDeps- , hasAuthor , hasLogfile- , patchFormat+ , quiet+ , verbose+ , enumeratePatches , fixRemoteRepos , fixUrl@@ -83,15 +59,53 @@ , fileHelpAuthor , environmentHelpEmail , getSubject- , getCharset , getInReplyTo , getCc , environmentHelpSendmail- , siblings , getOutput , getDate- , getReply- , applyAs++ -- * Re-exports+ , O.compress+ , O.diffAlgorithm+ , O.reorder+ , O.minimize+ , O.editDescription+ , O.externalMerge+ , O.maxCount+ , O.matchAny+ , O.withContext+ , O.happyForwarding+ , O.allowCaseDifferingFilenames+ , O.allowWindowsReservedFilenames+ , O.changesReverse+ , O.usePacks+ , O.onlyToFiles+ , O.amendUnrecord+ , O.verbosity+ , O.useCache+ , O.useIndex+ , O.umask+ , O.dryRun+ , O.runTest+ , O.testChanges+ , O.setScriptsExecutable+ , O.withWorkingDir+ , O.leaveTestDir+ , O.remoteRepos+ , O.cloneKind+ , O.workRepo+ , O.patchIndexNo+ , O.patchIndexYes+ , O.xmlOutput+ , O.selectDeps+ , O.author+ , O.reply+ , O.patchFormat+ , O.charset+ , O.siblings+ , O.applyAs+ , O.enumPatches ) where import Prelude ()@@ -107,28 +121,25 @@ import Control.Monad ( unless ) import System.Directory ( doesDirectoryExist, createDirectory ) import System.FilePath.Posix ( (</>) )+import System.Environment ( lookupEnv ) -import qualified Darcs.Patch.Match as MF ( MatchFlag(..) ) import Darcs.UI.External ( catchall ) import qualified Darcs.UI.Options.Flags as F ( DarcsFlag( .. ) ) import Darcs.UI.Options.Core import qualified Darcs.UI.Options.All as O-import Darcs.Util.Environment ( maybeGetEnv )-import Darcs.Util.Exception ( firstJustIO ) import Darcs.Util.File ( withCurrentDirectory ) import Darcs.Util.Prompt ( askUser , askUserListItem )-import Darcs.Util.Lock ( writeLocaleFile )+import Darcs.Util.Lock ( writeTextFile ) import Darcs.Repository.Prefs ( getPreflist , getGlobal , globalPrefsDirDoc , globalPrefsDir )-import Darcs.Util.ByteString ( decodeString ) import Darcs.Util.Global ( darcsdir ) import Darcs.Util.IsoDate ( getIsoDateTime, cleanLocalDate ) import Darcs.Util.Path@@ -146,20 +157,17 @@ type Config = [F.DarcsFlag] -compression :: Config -> O.Compression-compression = parseFlags O.compress+verbose :: Config -> Bool+verbose = (== O.Verbose) . parseFlags O.verbosity +quiet :: Config -> Bool+quiet = (== O.Quiet) . parseFlags O.verbosity+ remoteDarcs :: Config -> O.RemoteDarcs remoteDarcs = O.remoteDarcs . parseFlags O.network -reorder :: Config -> O.Reorder-reorder = parseFlags O.reorder--minimize :: Config -> Bool-minimize = parseFlags O.minimize--editDescription :: Config -> Bool-editDescription = parseFlags O.editDescription+enumeratePatches :: Config -> Bool+enumeratePatches = (== O.YesEnumPatches) . parseFlags O.enumPatches 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 =@@ -172,17 +180,15 @@ scanKnown O.YesLookForAdds O.YesIncludeBoring = O.ScanBoring diffingOpts :: Config -> (O.UseIndex, O.ScanKnown, O.DiffAlgorithm)-diffingOpts flags = diffOpts (useIndex flags) (lookForAdds flags) O.NoIncludeBoring (diffAlgorithm flags)--externalMerge :: Config -> O.ExternalMerge-externalMerge = parseFlags O.useExternalMerge+diffingOpts flags = diffOpts (O.useIndex ? flags) (lookForAdds flags)+ (parseFlags O.includeBoring flags) (O.diffAlgorithm ? flags) -- | This will become dis-entangled as soon as we inline these functions. wantGuiPause :: Config -> O.WantGuiPause wantGuiPause fs = if (hasDiffCmd fs || hasExternalMerge fs) && hasPause fs then O.YesWantGuiPause else O.NoWantGuiPause where- hasDiffCmd = isJust . O._diffCmd . parseFlags O.extDiff- hasExternalMerge = (/= O.NoExternalMerge) . parseFlags O.useExternalMerge+ hasDiffCmd = isJust . O.diffCmd . parseFlags O.extDiff+ hasExternalMerge = (/= O.NoExternalMerge) . parseFlags O.externalMerge hasPause = (== O.YesWantGuiPause) . parseFlags O.pauseForGui -- | Non-trivial interaction between options. Explicit @-i@ or @-a@ dominates,@@ -198,102 +204,30 @@ decide O.YesDryRun _ _ Nothing = False decide _ _ _ Nothing = def -maxCount :: Config -> Maybe Int-maxCount = parseFlags O.matchMaxcount- willRemoveLogFile :: Config -> Bool willRemoveLogFile = O._rmlogfile . parseFlags O.logfile -isUnified :: Config -> O.WithContext-isUnified = parseFlags O.withContext--doHappyForwarding :: Config -> Bool-doHappyForwarding = parseFlags O.happyForwarding- includeBoring :: Config -> Bool includeBoring cfg = case parseFlags O.includeBoring cfg of O.NoIncludeBoring -> False O.YesIncludeBoring -> True -doAllowCaseOnly :: Config -> Bool-doAllowCaseOnly = parseFlags O.allowCaseDifferingFilenames--doAllowWindowsReserved :: Config -> Bool-doAllowWindowsReserved = parseFlags O.allowWindowsReservedFilenames--doReverse :: Config -> Bool-doReverse = parseFlags O.changesReverse--usePacks :: Config -> Bool-usePacks = parseFlags O.usePacks--showChangesOnlyToFiles :: Config -> Bool-showChangesOnlyToFiles = parseFlags O.onlyToFiles--removeFromAmended :: Config -> Bool-removeFromAmended = parseFlags O.amendUnrecord--toMatchFlags :: Config -> [MF.MatchFlag]-toMatchFlags = parseFlags O.matchAny--verbosity :: Config -> O.Verbosity-verbosity = parseFlags O.verbosity--useCache :: Config -> O.UseCache-useCache = parseFlags O.useCache--umask :: Config -> O.UMask-umask = parseFlags O.umask--dryRun :: Config -> O.DryRun-dryRun = parseFlags O.dryRun--runPatchIndex :: Config -> O.WithPatchIndex-runPatchIndex = parseFlags O.patchIndex- lookForAdds :: Config -> O.LookForAdds lookForAdds = O.adds . parseFlags O.lookfor lookForReplaces :: Config -> O.LookForReplaces lookForReplaces = O.replaces . parseFlags O.lookfor -diffAlgorithm :: Config -> O.DiffAlgorithm-diffAlgorithm = parseFlags O.diffAlgorithm- lookForMoves :: Config -> O.LookForMoves lookForMoves = O.moves . parseFlags O.lookfor -runTest :: Config -> O.RunTest-runTest = parseFlags O.test--testChanges :: Config -> O.TestChanges-testChanges = parseFlags O.testChanges--setScriptsExecutable :: Config -> O.SetScriptsExecutable-setScriptsExecutable = parseFlags O.setScriptsExecutable--withWorkingDir :: Config -> O.WithWorkingDir-withWorkingDir = parseFlags O.useWorkingDir--leaveTestDir :: Config -> O.LeaveTestDir-leaveTestDir = parseFlags O.leaveTestDir--remoteRepos :: Config -> O.RemoteRepos-remoteRepos = parseFlags O.remoteRepos- setDefault :: Bool -> Config -> O.SetDefault setDefault defYes = maybe def noDef . parseFlags O.setDefault where def = if defYes then O.YesSetDefault False else O.NoSetDefault False noDef yes = if yes then O.YesSetDefault True else O.NoSetDefault True -cloneKind :: Config -> O.CloneKind-cloneKind = parseFlags O.partial--workRepo :: Config -> O.WorkRepo-workRepo = parseFlags O.workRepo- allowConflicts :: Config -> O.AllowConflicts-allowConflicts = maybe O.NoAllowConflicts id . parseFlags (O.conflicts O.NoAllowConflicts)+allowConflicts = maybe O.NoAllowConflicts id . parseFlags O.conflictsNo -- | Ugly. The alternative is to put the remoteRepos accessor into the IO monad, -- which is hardly better.@@ -394,10 +328,10 @@ as <- getEasyAuthor case as of [a] -> if alwaysAsk then- askForAuthor (fancyPrompt as) (fancyPrompt as)+ askForAuthor False (fancyPrompt as) (fancyPrompt as) else return a- [] -> askForAuthor shortPrompt longPrompt- _ -> askForAuthor (fancyPrompt as) (fancyPrompt as)+ [] -> askForAuthor True shortPrompt longPrompt+ _ -> askForAuthor False (fancyPrompt as) (fancyPrompt as) where shortPrompt = askUser "What is your email address? " longPrompt = askUser "What is your email address (e.g. Fred Bloggs <fred@example.net>)? "@@ -408,31 +342,42 @@ if str == "Other" then longPrompt else return str- askForAuthor askfn1 askfn2 = do- aminrepo <- doesDirectoryExist (darcsdir++"/prefs")+ askForAuthor storeGlobal askfn1 askfn2 = do+ aminrepo <- doesDirectoryExist (darcsdir </> "prefs") if aminrepo && store then do+ prefsdir <- if storeGlobal+ then tryGlobalPrefsDir+ else return $ darcsdir </> "prefs" putDocLn $ text "Each patch is attributed to its author, usually by email address (for" $$ text "example, `Fred Bloggs <fred@example.net>'). Darcs could not determine" $$ text "your email address, so you will be prompted for it." $$ text "" $$- text ("Your address will be stored in " ++ globalPrefsDirDoc ++ "author") $$- text "It will be used for all patches you record in ALL repositories." $$- text ("If you move that file to " ++ darcsdir </> "prefs" </> "author, it will") $$- text "be used for patches recorded in this repository only."+ text ("Your address will be stored in " ++ prefsdir)+ if prefsdir /= darcsdir </> "prefs" then+ putDocLn $+ text "It will be used for all patches you record in ALL repositories." $$+ text ("If you move that file to " ++ darcsdir </> "prefs" </> "author" ++ ", it will") $$+ text "be used for patches recorded in this repository only."+ else+ putDocLn $+ text "It will be used for all patches you record in this repository only." $$+ text ("If you move that file to " ++ globalPrefsDirDoc ++ "author, it will") $$+ text "be used for all patches recorded in ALL repositories." add <- askfn1- maybeprefsdir <- globalPrefsDir- prefsdir <- case maybeprefsdir of- Nothing -> do- putStrLn "WARNING: Global preference directory could not be found."- return $ darcsdir </> "prefs"- Just dir -> do exists <- doesDirectoryExist dir- unless exists $ createDirectory dir- return dir- writeLocaleFile (prefsdir </> "author") $+ writeTextFile (prefsdir </> "author") $ unlines ["# " ++ line | line <- fileHelpAuthor] ++ "\n" ++ add return add else askfn2+ tryGlobalPrefsDir = do+ maybeprefsdir <- globalPrefsDir+ case maybeprefsdir of+ Nothing -> do+ putStrLn "WARNING: Global preference directory could not be found."+ return $ darcsdir </> "prefs"+ Just dir -> do exists <- doesDirectoryExist dir+ unless exists $ createDirectory dir+ return dir -- | 'getEasyAuthor' tries to get the author name first from the repository preferences, -- then from global preferences, then from environment variables. Returns @[]@@@ -442,9 +387,9 @@ getEasyAuthor = firstNotNullIO [ (take 1 . nonblank) `fmap` getPreflist "author" , nonblank `fmap` getGlobal "author"- , maybeToList `fmap` maybeGetEnv "DARCS_EMAIL"- , maybeToList `fmap` maybeGetEnv "EMAIL"- ] >>= mapM decodeString+ , maybeToList `fmap` lookupEnv "DARCS_EMAIL"+ , maybeToList `fmap` lookupEnv "EMAIL"+ ] where nonblank = filter (not . null) -- this could perhaps be simplified with Control.Monad@@ -481,7 +426,7 @@ getSendmailCmd :: Config -> IO String getSendmailCmd fs = case parseFlags O.sendmailCmd fs of Just cmd -> return cmd- Nothing -> fmap (maybe "" id) $ firstJustIO [ maybeGetEnv "SENDMAIL" ]+ Nothing -> fmap (maybe "" id) $ lookupEnv "SENDMAIL" -- | Accessor for output option getOutput :: Config -> FilePath -> Maybe AbsolutePathOrStd@@ -489,9 +434,6 @@ go (O.Output ap) = ap go (O.OutputAutoName ap) = makeAbsoluteOrStd ap fp -getCharset :: Config -> Maybe String-getCharset = parseFlags O.charset- -- |'getSubject' takes a list of flags and returns the subject of the mail -- to be sent by @darcs send@. Looks for a subject specified by -- @Subject \"subject\"@ in that list of flags, if any.@@ -509,33 +451,8 @@ getInReplyTo :: Config -> Maybe String getInReplyTo = O._inReplyTo . parseFlags O.headerFields -getReply :: Config -> Maybe String-getReply = parseFlags O.reply---- | 'flagsToSiblings' collects the contents of all @Sibling@ flags in a list of flags.-siblings :: Config -> [AbsolutePath]-siblings = parseFlags O.siblings--useIndex :: Config -> O.UseIndex-useIndex = parseFlags O.useIndex--hasSummary :: O.Summary -> Config -> O.Summary-hasSummary def = maybe def id . parseFlags O.summary--hasXmlOutput :: Config -> O.XmlOutput-hasXmlOutput = parseFlags O.xmloutput--selectDeps :: Config -> O.SelectDeps-selectDeps = parseFlags O.selectDeps+hasXmlOutput :: Config -> Bool+hasXmlOutput = (== O.YesXml) . parseFlags O.xmlOutput hasLogfile :: Config -> Maybe AbsolutePath hasLogfile = O._logfile . parseFlags O.logfile--hasAuthor :: Config -> Maybe String-hasAuthor = parseFlags O.author--patchFormat :: Config -> O.PatchFormat-patchFormat = parseFlags O.patchFormat--applyAs :: Config -> Maybe String-applyAs = parseFlags O.applyAs
@@ -1,186 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}--- Copyright (C) 2002-2004 David Roundy------ This program is free software; you can redistribute it and/or modify--- it under the terms of the GNU General Public License as published by--- the Free Software Foundation; either version 2, or (at your option)--- any later version.------ This program is distributed in the hope that it will be useful,--- but WITHOUT ANY WARRANTY; without even the implied warranty of--- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the--- GNU General Public License for more details.------ You should have received a copy of the GNU General Public License--- along with this program; see the file COPYING. If not, write to--- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,--- Boston, MA 02110-1301, USA.--{-# LANGUAGE CPP, TypeOperators #-}---- | 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.Repository.Flags ( DryRun(..) )-import Darcs.Util.Text ( sentence, quote )-import Darcs.Util.Printer--cmdDescription :: String-cmdDescription =- "Prepare a bundle of patches to be applied to some target repository."--cmdHelp :: String-cmdHelp = unlines- [ "Send is used to prepare a bundle of patches that can be applied to a target"- , "repository. Send accepts the URL of the repository as an argument. When"- , "called without an argument, send will use the most recent repository that"- , "was either pushed to, pulled from or sent to. By default, the patch bundle"- , "is saved to a file, although you may directly send it by mail."- , ""- , "The `--output`, `--output-auto-name`, and `--to` flags determine"- , "what darcs does with the patch bundle after creating it. If you provide an"- , "`--output` argument, the patch bundle is saved to that file. If you"- , "specify `--output-auto-name`, the patch bundle is saved to a file with an"- , "automatically generated name. If you give one or more `--to` arguments,"- , "the bundle of patches is sent to those locations. The locations may either"- , "be email addresses or urls that the patch should be submitted to via HTTP."- , ""- , "If you provide the `--mail` flag, darcs will look at the contents"- , "of the `_darcs/prefs/email` file in the target repository (if it exists),"- , "and send the patch by email to that address. In this case, you may use"- , "the `--cc` option to specify additional recipients without overriding the"- , "default repository email address."- , ""- , "If `_darcs/prefs/post` exists in the target repository, darcs will"- , "upload to the URL contained in that file, which may either be a"- , "`mailto:` URL, or an `http://` URL. In the latter case, the"- , "patch is posted to that URL."- , ""- , "If there is no email address associated with the repository, darcs will"- , "prompt you for an email address."- , ""- , "Use the `--subject` flag to set the subject of the e-mail to be sent."- , "If you don't provide a subject on the command line, darcs will make one up"- , "based on names of the patches in the patch bundle."- , ""- , "Use the `--in-reply-to` flag to set the In-Reply-To and References headers"- , "of the e-mail to be sent. By default no additional headers are included so"- , "e-mail will not be treated as reply by mail readers."- , ""- , "If you want to include a description or explanation along with the bundle"- , "of patches, you need to specify the `--edit-description` flag, which"- , "will cause darcs to open up an editor with which you can compose a message"- , "to go along with your patches."- , ""- , "If you want to use a command different from the default one for sending"- , "email, you need to specify a command line with the `--sendmail-command`"- , "option. The command line can contain some format specifiers which are"- , "replaced by the actual values. Accepted format specifiers are `%s` for"- , "subject, `%t` for to, `%c` for cc, `%b` for the body of the mail, `%f` for"- , "from, `%a` for the patch bundle and the same specifiers in uppercase for the"- , "URL-encoded values."- , "Additionally you can add `%<` to the end of the command line if the command"- , "expects the complete email message on standard input. E.g. the command lines"- , "for evolution and msmtp look like this:"- , ""- , " evolution \"mailto:%T?subject=%S&attach=%A&cc=%C&body=%B\""- , " msmtp -t %<"- , ""- , "Do not confuse the `--author` options with the return address"- , "that `darcs send` will set for your patch bundle."- , ""- , "For example, if you have two email addresses A and B:"- , ""- , "* If you use `--author A` but your machine is configured to send mail from"- , " address B by default, then the return address on your message will be B."- , "* If you use `--from A` and your mail client supports setting the"- , " From: address arbitrarily (some non-Unix-like mail clients, especially,"- , " may not support this), then the return address will be A; if it does"- , " not support this, then the return address will be B."- , "* If you supply neither `--from` nor `--author` then the return"- , " address will be B."- , ""- , "In addition, unless you specify the sendmail command with"- , "`--sendmail-command`, darcs sends email using the default email"- , "command on your computer. This default command is determined by the"- , "`configure` script. Thus, on some non-Unix-like OSes,"- , "`--from` is likely to not work at all."- ]--cannotSendToSelf :: String-cannotSendToSelf = "Can't send to current repository! Did you mean send --context?"--creatingPatch :: String -> Doc-creatingPatch repodir = "Creating patch to" <+> text (quote repodir) <> "..."--noWorkingSendmail :: Doc-noWorkingSendmail = "No working sendmail instance on your machine!"--nothingSendable :: Doc-nothingSendable = "No recorded local changes to send!"--selectionIs :: [Doc] -> Doc-selectionIs descs = text "We have the following patches to send:" $$ vcat descs--selectionIsNull :: Doc-selectionIsNull = text "You don't want to send any patches, and that's fine with me!"--emailBackedUp :: String -> Doc-emailBackedUp mf = sentence $ "Email body left in" <+> text mf--promptCharSetWarning :: String -> String-promptCharSetWarning msg = "Warning: " ++ msg ++ " Send anyway?"--charsetAborted :: Doc-charsetAborted = "Aborted. You can specify charset with the --charset option."--charsetCouldNotGuess :: String-charsetCouldNotGuess = "darcs could not guess the charset of your mail."--currentEncodingIs :: String -> String-currentEncodingIs e = "Current locale encoding: " ++ e--charsetUtf8MailDiffLocale :: String-charsetUtf8MailDiffLocale = "your mail is valid UTF-8 but your locale differs."--aborted :: Doc-aborted = "Aborted."--success :: String -> String -> Doc-success to cc = sentence $- "Successfully sent patch bundle to:" <+> text to <+> copies cc- where- copies "" = ""- copies x = "and cc'ed" <+> text x--postingPatch :: String -> Doc-postingPatch url = "Posting patch to" <+> text url--wroteBundle :: FilePathLike a => a -> Doc-wroteBundle a = sentence $ "Wrote patch to" <+> text (toFilePath a)--savedButNotSent :: String -> Doc-savedButNotSent to =- text ("The usual recipent for this bundle is: " ++ to)- $$ text "To send it automatically, make sure sendmail is working,"- <+> text "and add 'send mail' to _darcs/prefs/defaults or"- <+> text " ~/.darcs/defaults"--willSendTo :: DryRun -> [String] -> Doc-willSendTo dr addresses =- "Patch bundle" <+> will <+> " be sent to:" <+> text (unwords addresses)- where- will = case dr of { YesDryRun -> "would"; NoDryRun -> "will" }--promptTarget :: String-promptTarget = "What is the target email address? "--aboutToEdit :: FilePath -> String-aboutToEdit file = "About to edit file " ++ file--promptNoDescriptionChange :: String-promptNoDescriptionChange = "File content did not change. Continue anyway?"
@@ -1,6 +1,5 @@ module Darcs.UI.Options ( module Darcs.UI.Options.Core- , module Darcs.UI.Options.Markdown , DarcsOption , PrimDarcsOption , DarcsOptDescr@@ -10,22 +9,14 @@ import Prelude () import Darcs.Prelude -import Data.Functor.Compose-import System.Console.GetOpt+import Data.Functor.Compose ( getCompose )+import System.Console.GetOpt ( OptDescr ) -import Darcs.UI.Options.All+import Darcs.UI.Options.All ( DarcsOption ) import Darcs.UI.Options.Core-import Darcs.UI.Options.Markdown import Darcs.UI.Options.Util ( DarcsOptDescr, PrimDarcsOption ) import Darcs.Util.Path ( AbsolutePath ) --- * Type instantiations---- | The @instance Functor OptDescr@ was introduced only in base-4.7.0.0, which is--- why we implement it here manually.+-- | Instantiate a 'DarcsOptDescr' with an 'AbsolutePath' optDescr :: AbsolutePath -> DarcsOptDescr f -> OptDescr f-optDescr path = omap ($path) . getCompose where- omap f (Option s l a h) = Option s l (amap f a) h- amap f (NoArg a) = NoArg (f a)- amap f (ReqArg mkF n) = ReqArg (fmap f mkF) n- amap f (OptArg mkF n) = OptArg (fmap f mkF) n+optDescr path = fmap ($ path) . getCompose
@@ -28,6 +28,9 @@ module Darcs.UI.Options.All ( DarcsOption + -- conversion to 'Bool'+ , YesNo (..)+ -- root , RootAction (..) , rootActions@@ -40,6 +43,8 @@ , verbosity , timings , anyVerbosity+ , HooksConfig (..) -- re-export+ , HookConfig (..) -- re-export , preHook , postHook , hooks@@ -48,7 +53,7 @@ -- interactivity , XmlOutput (..)- , xmloutput+ , xmlOutput , DryRun (..) -- re-export , dryRun , dryRunXml@@ -56,23 +61,24 @@ , pipe , WantGuiPause (..) -- re-export , pauseForGui- , askdeps+ , askDeps -- patch selection , module Darcs.UI.Options.Matching -- re-export , SelectDeps (..) , selectDeps , changesReverse- , matchMaxcount+ , maxCount -- local or remote repo(s) , WorkRepo (..) -- re-export , workRepo- , workingRepoDir+ , repoDir , RemoteRepos (..) -- re-export , remoteRepos , possiblyRemoteRepo , reponame+ , NotInRemote (..) , notInRemote , notInRemoteFlagName , RepoCombinator (..)@@ -80,7 +86,7 @@ , allowUnrelatedRepos , justThisRepo , WithWorkingDir (..) -- re-export- , useWorkingDir+ , withWorkingDir , SetDefault (..) -- re-export , setDefault @@ -99,11 +105,13 @@ , LookForMoves (..) -- re-export , LookForReplaces (..) -- re-export , lookfor+ , lookforadds+ , lookforreplaces+ , lookformoves -- files to consider , UseIndex (..) -- re-export , ScanKnown (..) -- re-export- , diffing , IncludeBoring (..) , includeBoring , allowProblematicFilenames@@ -118,7 +126,6 @@ , diffAlgorithm , WithContext (..) , withContext- , unidiff , ExternalDiff (..) , extDiff @@ -126,7 +133,7 @@ , TestChanges (..) , testChanges , RunTest (..) -- re-export- , test+ , runTest , LeaveTestDir (..) -- re-export , leaveTestDir @@ -151,16 +158,17 @@ -- merging patches , AllowConflicts (..) -- re-export- , conflicts+ , conflictsNo+ , conflictsYes , ExternalMerge (..) -- re-export- , useExternalMerge+ , externalMerge -- optimizations , Compression (..) -- re-export , compress , usePacks , WithPatchIndex (..) -- re-export- , patchIndex+ , patchIndexNo , patchIndexYes , Reorder (..) -- re-export , reorder@@ -172,6 +180,7 @@ , output , Summary (..) , summary+ , maybeSummary , RemoteDarcs (..) -- re-export , NetworkOptions (..) , network@@ -188,12 +197,11 @@ , selectAuthor -- annotate- , humanReadable , machineReadable -- clone , CloneKind (..)- , partial+ , cloneKind -- dist , distname@@ -225,13 +233,16 @@ , pending , nullFlag + -- show repo+ , EnumPatches (..)+ , enumPatches+ -- gzcrcs , GzcrcsAction (..) , gzcrcsActions -- optimize , siblings- , reorderPatches , optimizePatchIndex ) where @@ -241,7 +252,6 @@ import Prelude hiding ( (^) ) import Data.Char ( isDigit ) import Data.List ( intercalate )-import Data.Maybe ( listToMaybe ) import Darcs.Repository.Flags ( Compression (..)@@ -271,6 +281,8 @@ , WithWorkingDir (..) , PatchFormat (..) , IncludeBoring (..)+ , HooksConfig (..)+ , HookConfig (..) ) import qualified Darcs.UI.Options.Flags as F ( DarcsFlag(..) )@@ -288,6 +300,79 @@ type RawDarcsOption = forall v. v -> RawOptSpec Flag v +-- * Conversion to 'Bool'++class YesNo a where+ yes :: a -> Bool+ no :: a -> Bool+ no = not . yes++instance YesNo Compression where+ yes NoCompression = False+ yes GzipCompression = True++instance YesNo WithPatchIndex where+ yes NoPatchIndex = False+ yes YesPatchIndex = True++instance YesNo Reorder where+ yes NoReorder = False+ yes Reorder = True++instance YesNo UseCache where+ yes NoUseCache = False+ yes YesUseCache = True++instance YesNo DryRun where+ yes NoDryRun = False+ yes YesDryRun = True++instance YesNo LookForAdds where+ yes NoLookForAdds = False+ yes YesLookForAdds = True++instance YesNo LookForReplaces where+ yes NoLookForReplaces = False+ yes YesLookForReplaces = True++instance YesNo LookForMoves where+ yes NoLookForMoves = False+ yes YesLookForMoves = True++instance YesNo IncludeBoring where+ yes NoIncludeBoring = False+ yes YesIncludeBoring = True++instance YesNo RunTest where+ yes NoRunTest = False+ yes YesRunTest = True++instance YesNo SetScriptsExecutable where+ yes NoSetScriptsExecutable = False+ yes YesSetScriptsExecutable = True++instance YesNo LeaveTestDir where+ yes NoLeaveTestDir = False+ yes YesLeaveTestDir = True++instance YesNo UseIndex where+ yes IgnoreIndex = False+ yes UseIndex = True++instance YesNo WantGuiPause where+ yes NoWantGuiPause = False+ yes YesWantGuiPause = True++instance YesNo WithWorkingDir where+ yes NoWorkingDir = False+ yes WithWorkingDir = True++data EnumPatches = NoEnumPatches | YesEnumPatches deriving (Eq, Show)++instance YesNo EnumPatches where+ yes NoEnumPatches = False+ yes YesEnumPatches = True+ -- * Root command -- | Options for darcs iself that act like sub-commands.@@ -295,13 +380,13 @@ rootActions :: PrimDarcsOption (Maybe RootAction) rootActions = withDefault Nothing- [ RawNoArg ['h'] ["help", "overview"] F.Help (Just RootHelp)+ [ RawNoArg ['h'] ["help"] F.Help (Just RootHelp) "show a brief description of all darcs commands and top-level options"- , RawNoArg ['v'] ["version"] F.Version (Just Version) "show the darcs version"+ , RawNoArg ['v','V'] ["version"] F.Version (Just Version) "show the darcs version" , RawNoArg [] ["exact-version"] F.ExactVersion (Just ExactVersion) "show the exact darcs version" -- the switch --commands is here for compatibility only- , RawNoArg [] ["commands","list-options"] F.ListCommands (Just ListCommands)+ , RawNoArg [] ["commands"] F.ListCommands (Just ListCommands) "show plain list of available options and commands, for auto-completion" ] @@ -342,15 +427,22 @@ -- ** Hooks -hooks :: DarcsOption a (Maybe String -> Bool -> Maybe String -> Bool -> a)-hooks = preHook ^ postHook+hooks :: DarcsOption a (HooksConfig -> a)+hooks = imap (Iso fw bw) $ preHook ^ postHook where+ fw k (HooksConfig pr po) = k pr po+ bw k pr po = k (HooksConfig pr po) -preHook :: DarcsOption a (Maybe String -> Bool -> a)-preHook = prehookCmd ^ hookPrompt "prehook" F.AskPrehook F.RunPrehook+hookIso :: Iso (Maybe String -> Bool -> a) (HookConfig -> a)+hookIso = (Iso fw bw) where+ fw k (HookConfig c p) = k c p+ bw k c p = k (HookConfig c p) -postHook :: DarcsOption a (Maybe String -> Bool -> a)-postHook = posthookCmd ^ hookPrompt "posthook" F.AskPosthook F.RunPosthook+preHook :: DarcsOption a (HookConfig -> a)+preHook = imap hookIso $ prehookCmd ^ hookPrompt "prehook" F.AskPrehook F.RunPrehook +postHook :: DarcsOption a (HookConfig -> a)+postHook = imap hookIso $ posthookCmd ^ hookPrompt "posthook" F.AskPosthook F.RunPosthook+ prehookCmd :: PrimDarcsOption (Maybe String) prehookCmd = withDefault Nothing [ RawStrArg [] ["prehook"] F.PrehookCmd unF Just unV@@ -397,30 +489,27 @@ data XmlOutput = NoXml | YesXml deriving (Eq, Show) -xmloutput :: PrimDarcsOption XmlOutput-xmloutput = withDefault NoXml [__xmloutput YesXml]+instance YesNo XmlOutput where+ yes NoXml = False+ yes YesXml = True +xmlOutput :: PrimDarcsOption XmlOutput+xmlOutput = withDefault NoXml [__xmloutput YesXml]+ __xmloutput :: RawDarcsOption __xmloutput val = RawNoArg [] ["xml-output"] F.XMLOutput val "generate XML formatted output" --- | NOTE: I'd rather work to have no uses of dryRunNoxml, so that any time--- --dry-run is a possibility, automated users can examine the results more--- easily with --xml.------ See also issue2397.+-- | TODO someone wrote here long ago that any time --dry-run is a possibility+-- automated users should be able to examine the results more+-- easily with --xml. See also issue2397.+-- dryRun w/o xml is currently used in add, pull, and repair.+ dryRun :: PrimDarcsOption DryRun-dryRun = (imap . cps) (Iso fw bw) $ singleNoArg [] ["dry-run"] F.DryRun "don't actually take the action"- where- fw True = YesDryRun- fw False = NoDryRun- bw YesDryRun = True- bw NoDryRun = False+dryRun = withDefault NoDryRun+ [ RawNoArg [] ["dry-run"] F.DryRun YesDryRun "don't actually take the action" ] dryRunXml :: DarcsOption a (DryRun -> XmlOutput -> a)-dryRunXml = dryRun ^ xmloutput--__dryrun :: RawDarcsOption-__dryrun val = RawNoArg [] ["dry-run"] F.DryRun val "don't actually take the action"+dryRunXml = dryRun ^ xmlOutput pipe :: PrimDarcsOption Bool pipe = singleNoArg [] ["pipe"] F.Pipe "ask user interactively for the patch metadata"@@ -437,8 +526,8 @@ , RawNoArg [] ["no-pause-for-gui"] F.NoPauseForGui NoWantGuiPause "return immediately after external diff or merge command finishes" ] -askdeps :: PrimDarcsOption Bool-askdeps = withDefault False+askDeps :: PrimDarcsOption Bool+askDeps = withDefault False [ RawNoArg [] ["ask-deps"] F.AskDeps True "manually select dependencies" , RawNoArg [] ["no-ask-deps"] F.NoAskDeps False "automatically select dependencies" ] @@ -465,22 +554,28 @@ -- should take either a plain 'String' argument (leaving it to a later stage -- to parse the 'String' to an 'Int'), or else a @'Maybe' 'Int'@, taking -- the possibility of a failed parse into account.-matchMaxcount :: PrimDarcsOption (Maybe Int)-matchMaxcount = OptSpec {..} where- ounparse k (Just n) = k [ F.MaxCount n ]- ounparse k Nothing = k []- oparse k fs = k $ listToMaybe [ s | F.MaxCount s <- fs ]- ocheck fs = case [ "--max-count="++show n | F.MaxCount n <- fs ] of- cfs@(_:_:_) -> ["conflicting flags: " ++ intercalate ", " cfs]- _ -> []- odesc = [ strArg [] ["max-count"] (F.MaxCount . toInt) "NUMBER"- "return only NUMBER results" ]- toInt s = if not (null s) && all isDigit s then read s else (-1)+maxCount :: PrimDarcsOption (Maybe Int)+maxCount = (withDefault Nothing+ [ RawStrArg [] ["max-count"] F.MaxCount unF toV unV "NUMBER"+ "return only NUMBER results" ])+ {ocheck=check}+ where+ unF f = [ s | F.MaxCount s <- [f] ]+ unV x = [ show s | Just s <- [x] ]+ toV s = if good s then Just (read s) else Nothing+ check fs =+ [ "invalid argument to --max-count: '"++s++"'" | s <- args, not (good s) ] +++ if length args > 1+ then ["conflicting flags: " ++ intercalate ", " (map ("--max-count="++) args)]+ else []+ where+ args = [ s | F.MaxCount s <- fs ]+ good s = not (null s) && all isDigit s -- * Local or remote repo workRepo :: PrimDarcsOption WorkRepo-workRepo = imap (Iso fw bw) $ workingRepoDir ^ possiblyRemoteRepo where+workRepo = imap (Iso fw bw) $ repoDir ^ possiblyRemoteRepo where fw k (WorkRepoDir s) = k (Just s) Nothing fw k (WorkRepoPossibleURL s) = k Nothing (Just s) fw k WorkRepoCurrentDir = k Nothing Nothing@@ -488,8 +583,8 @@ bw k Nothing (Just s) = k (WorkRepoPossibleURL s) bw k Nothing Nothing = k WorkRepoCurrentDir -workingRepoDir :: PrimDarcsOption (Maybe String)-workingRepoDir = singleStrArg [] ["repodir"] F.WorkRepoDir arg "DIRECTORY"+repoDir :: PrimDarcsOption (Maybe String)+repoDir = singleStrArg [] ["repodir"] F.WorkRepoDir arg "DIRECTORY" "specify the repository directory in which to run" where arg (F.WorkRepoDir s) = Just s arg _ = Nothing@@ -518,13 +613,21 @@ notInRemoteFlagName :: String notInRemoteFlagName = "not-in-remote" -notInRemote :: PrimDarcsOption [Maybe String]-notInRemote =+data NotInRemote+ = NotInDefaultRepo+ | NotInRemotePath String++notInRemote :: PrimDarcsOption [NotInRemote]+notInRemote = (imap . cps) (Iso (map fw) (map bw)) $ multiOptStrArg [] [notInRemoteFlagName] F.NotInRemote args "URL/PATH" $ "select all patches not in the default push/pull repository or at " ++ "location URL/PATH" where args fs = [s | F.NotInRemote s <- fs]+ fw (Just s) = NotInRemotePath s+ fw Nothing = NotInDefaultRepo+ bw (NotInRemotePath s) = Just s+ bw NotInDefaultRepo = Nothing data RepoCombinator = Intersection | Union | Complement deriving (Eq, Show) @@ -546,8 +649,8 @@ "Limit the check or repair to the current repo" -- | convert, clone, init-useWorkingDir :: PrimDarcsOption WithWorkingDir-useWorkingDir = withDefault WithWorkingDir+withWorkingDir :: PrimDarcsOption WithWorkingDir+withWorkingDir = withDefault WithWorkingDir [ RawNoArg [] ["with-working-dir"] F.UseWorkingDir WithWorkingDir "Create a working directory (normal repository)" , RawNoArg [] ["no-working-dir"] F.UseNoWorkingDir NoWorkingDir@@ -625,12 +728,12 @@ } lookfor :: PrimDarcsOption LookFor-lookfor = imap (Iso fw bw) (lookforadds ^ lookforreplaces ^ lookformoves) where+lookfor = imap (Iso fw bw) (lookforadds NoLookForAdds ^ lookforreplaces ^ lookformoves) where fw k (LookFor a r m) = k a r m bw k a r m = k (LookFor a r m) -lookforadds :: PrimDarcsOption LookForAdds-lookforadds = withDefault NoLookForAdds+lookforadds :: LookForAdds -> PrimDarcsOption LookForAdds+lookforadds def = withDefault def [ RawNoArg ['l'] ["look-for-adds"] F.LookForAdds YesLookForAdds "look for (non-boring) files that could be added" , RawNoArg [] ["dont-look-for-adds","no-look-for-adds"] F.NoLookForAdds NoLookForAdds@@ -654,12 +757,6 @@ -- * Files to consider -diffing :: PrimDarcsOption (UseIndex, ScanKnown, DiffAlgorithm)-diffing = imap (Iso curry3 uncurry3) $ useIndex ^ scanKnown ^ diffAlgorithm- where- uncurry3 k x y z = k (x,y,z)- curry3 k (x,y,z) = k x y z- useIndex :: PrimDarcsOption UseIndex useIndex = (imap . cps) (Iso fw bw) ignoreTimes where fw False = UseIndex@@ -667,15 +764,6 @@ bw UseIndex = False bw IgnoreIndex = True -scanKnown :: PrimDarcsOption ScanKnown-scanKnown = imap (Iso fw bw) $ lookforadds ^ includeBoring where- 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 NoIncludeBoring = k ScanAll- bw k YesLookForAdds YesIncludeBoring = k ScanBoring- includeBoring :: PrimDarcsOption IncludeBoring includeBoring = withDefault NoIncludeBoring [ RawNoArg [] ["boring"] F.Boring YesIncludeBoring "don't skip boring files"@@ -729,6 +817,10 @@ data WithContext = NoContext | YesContext deriving (Eq, Show) +instance YesNo WithContext where+ yes NoContext = False+ yes YesContext = True+ withContext :: PrimDarcsOption WithContext withContext = (imap . cps) (Iso fw bw) $ withDefault False [ RawNoArg ['u'] ["unified"] F.Unified True@@ -740,42 +832,46 @@ bw NoContext = False bw YesContext = True -unidiff :: PrimDarcsOption Bool-unidiff = withDefault True- [ RawNoArg ['u'] ["unified"] F.Unified True "pass -u option to diff"- , RawNoArg [] ["no-unified"] F.NonUnified False "output patch in diff's dumb format" ]--data ExternalDiff = ExternalDiff { _diffCmd :: Maybe String, _diffOpts :: [String] } deriving (Eq, Show)+data ExternalDiff = ExternalDiff+ { diffCmd :: Maybe String+ , diffOpts :: [String]+ , diffUnified :: Bool+ } deriving (Eq, Show) extDiff :: PrimDarcsOption ExternalDiff-extDiff = imap (Iso fw bw) $ extDiffCmd ^ extDiffOpts where- fw k (ExternalDiff cmd opts) = k cmd opts- bw k cmd opts = k (ExternalDiff cmd opts)+extDiff = imap (Iso fw bw) $ __extDiffCmd ^ __extDiffOpts ^ __unidiff where+ fw k (ExternalDiff cmd opts uni) = k cmd opts uni+ bw k cmd opts uni = k (ExternalDiff cmd opts uni) -extDiffCmd :: PrimDarcsOption (Maybe String)-extDiffCmd = singleStrArg [] ["diff-command"] F.DiffCmd arg "COMMAND"+__extDiffCmd :: PrimDarcsOption (Maybe String)+__extDiffCmd = singleStrArg [] ["diff-command"] F.DiffCmd arg "COMMAND" "specify diff command (ignores --diff-opts)" where arg (F.DiffCmd s) = Just s arg _ = Nothing -extDiffOpts :: PrimDarcsOption [String]-extDiffOpts = multiStrArg [] ["diff-opts"] F.DiffFlags mkV "OPTIONS"+__extDiffOpts :: PrimDarcsOption [String]+__extDiffOpts = multiStrArg [] ["diff-opts"] F.DiffFlags mkV "OPTIONS" "options to pass to diff" where mkV fs = [ s | F.DiffFlags s <- fs ] +__unidiff :: PrimDarcsOption Bool+__unidiff = withDefault True+ [ RawNoArg ['u'] ["unified"] F.Unified True "pass -u option to diff"+ , RawNoArg [] ["no-unified"] F.NonUnified False "output patch in diff's dumb format" ]+ -- * Runnign tests data TestChanges = NoTestChanges | YesTestChanges LeaveTestDir deriving (Eq) testChanges :: PrimDarcsOption TestChanges-testChanges = imap (Iso fw bw) $ test ^ leaveTestDir where+testChanges = imap (Iso fw bw) $ runTest ^ leaveTestDir where fw k NoTestChanges = k NoRunTest {- undefined -} YesLeaveTestDir fw k (YesTestChanges ltd) = k YesRunTest ltd bw k NoRunTest _ = k NoTestChanges bw k YesRunTest ltd = k (YesTestChanges ltd) -test :: PrimDarcsOption RunTest-test = withDefault NoRunTest+runTest :: PrimDarcsOption RunTest+runTest = withDefault NoRunTest [ RawNoArg [] ["test"] F.Test YesRunTest "run the test script" , RawNoArg [] ["no-test"] F.NoTest NoRunTest "don't run the test script" ] @@ -805,8 +901,8 @@ arg _ = Nothing to :: PrimDarcsOption [String]-to = multiStrArg [] ["to"] F.Target mkV "EMAIL" "specify destination email"- where mkV fs = [ s | F.Target s <- fs ]+to = multiStrArg [] ["to"] F.To mkV "EMAIL" "specify destination email"+ where mkV fs = [ s | F.To s <- fs ] cc :: PrimDarcsOption [String] cc = multiStrArg [] ["cc"] F.Cc mkV "EMAIL" "mail results to additional EMAIL(s)"@@ -937,25 +1033,28 @@ -- * Merging patches --- applyConflictOptions = conflicts NoAllowConflicts--- pullConflictOptions = conflicts YesAllowConflictsAndMark+-- | push, apply, rebase apply: default to 'NoAllowConflicts'+conflictsNo :: PrimDarcsOption (Maybe AllowConflicts)+conflictsNo = conflicts NoAllowConflicts +-- | pull, rebase pull: default to 'YesAllowConflictsAndMark'+conflictsYes :: PrimDarcsOption (Maybe AllowConflicts)+conflictsYes = conflicts YesAllowConflictsAndMark+ conflicts :: AllowConflicts -> PrimDarcsOption (Maybe AllowConflicts) conflicts def = withDefault (Just def) [ RawNoArg [] ["mark-conflicts"] F.MarkConflicts (Just YesAllowConflictsAndMark) "mark conflicts" , RawNoArg [] ["allow-conflicts"] F.AllowConflicts (Just YesAllowConflicts) "allow conflicts, but don't mark them"--- , RawNoArg [] ["no-resolve-conflicts"]--- NoAllowConflicts "equivalent to --dont-allow-conflicts, for backwards compatibility" , RawNoArg [] ["dont-allow-conflicts","no-allow-conflicts","no-resolve-conflicts"] F.NoAllowConflicts (Just NoAllowConflicts) "fail if there are patches that would create conflicts" , RawNoArg [] ["skip-conflicts"] F.SkipConflicts Nothing "filter out any patches that would create conflicts" ] -- Technically not an isomorphism, see 'sendmailIso'.-useExternalMerge :: PrimDarcsOption ExternalMerge-useExternalMerge = imap (Iso fw bw) $ singleStrArg [] ["external-merge"] F.ExternalMerge arg+externalMerge :: PrimDarcsOption ExternalMerge+externalMerge = imap (Iso fw bw) $ singleStrArg [] ["external-merge"] F.ExternalMerge arg "COMMAND" "use external tool to merge conflicts" where arg (F.ExternalMerge s) = Just s@@ -978,8 +1077,8 @@ , RawNoArg [] ["no-packs"] F.NoPacks False "don't use repository packs" ] -- for init, clone and convert: patch index disabled by default-patchIndex :: PrimDarcsOption WithPatchIndex-patchIndex = withDefault NoPatchIndex [__patchIndex YesPatchIndex, __noPatchIndex NoPatchIndex]+patchIndexNo :: PrimDarcsOption WithPatchIndex+patchIndexNo = withDefault NoPatchIndex [__patchIndex YesPatchIndex, __noPatchIndex NoPatchIndex] -- for log and annotate: patch index enabled by default patchIndexYes :: PrimDarcsOption WithPatchIndex@@ -1025,8 +1124,23 @@ data Summary = NoSummary | YesSummary deriving (Eq, Show) -summary :: PrimDarcsOption (Maybe Summary)-summary = withDefault Nothing+instance YesNo Summary where+ yes NoSummary = False+ yes YesSummary = True++-- all commands except whatsnew+summary :: PrimDarcsOption Summary+summary = (imap . cps) (Iso fw bw) $ maybeSummary Nothing+ where+ fw Nothing = NoSummary+ fw (Just NoSummary) = NoSummary+ fw (Just YesSummary) = YesSummary+ bw NoSummary = Nothing+ bw YesSummary = Just YesSummary++-- needed for whatsnew+maybeSummary :: Maybe Summary -> PrimDarcsOption (Maybe Summary)+maybeSummary def = withDefault def [ RawNoArg ['s'] ["summary"] F.Summary (Just YesSummary) "summarize changes" , RawNoArg [] ["no-summary"] F.NoSummary (Just NoSummary) "don't summarize changes" ] @@ -1091,24 +1205,21 @@ -- ** annotate --- | TODO: These should be mutually exclusive, but are they? The code is almost inscrutable.-humanReadable :: PrimDarcsOption Bool-humanReadable = withDefault False [__humanReadable True]+machineReadable :: PrimDarcsOption Bool+machineReadable = withDefault False+ [ __humanReadable False+ , __machineReadable True ] __humanReadable :: RawDarcsOption __humanReadable val = RawNoArg [] ["human-readable"] F.HumanReadable val "give human-readable output" --- | See above.-machineReadable :: PrimDarcsOption Bool-machineReadable = withDefault False [__machineReadable True]- __machineReadable :: RawDarcsOption __machineReadable val = RawNoArg [] ["machine-readable"] F.MachineReadable val "give machine-readable output" -- ** clone -partial :: PrimDarcsOption CloneKind-partial = withDefault NormalClone+cloneKind :: PrimDarcsOption CloneKind+cloneKind = withDefault NormalClone [ RawNoArg [] ["lazy"] F.Lazy LazyClone "get patch files only as needed" , RawNoArg [] ["complete"] F.Complete CompleteClone "get a complete copy of the repository" ] @@ -1155,11 +1266,18 @@ -- ** log -data ChangesFormat = HumanReadable | MachineReadable | 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"+ [ RawNoArg [] ["context"] F.GenContext (Just GenContext) "give output suitable for clone --context" , __xmloutput (Just GenXml) , __humanReadable (Just HumanReadable) , __machineReadable (Just MachineReadable)@@ -1191,7 +1309,7 @@ , RawNoArg [] ["backoff"] F.Backoff Backoff "exponential backoff search" , RawNoArg [] ["bisect"] F.Bisect Bisect "binary instead of linear search" ] --- ** show files/index+-- ** show files files :: PrimDarcsOption Bool files = withDefault True@@ -1206,12 +1324,21 @@ pending :: PrimDarcsOption Bool pending = withDefault True [ RawNoArg [] ["pending"] F.Pending True "reflect pending patches in output"- , RawNoArg [] ["no-pending"] F.NoPending False "only included recorded patches in output" ]+ , RawNoArg [] ["no-pending"] F.NoPending False "only include recorded patches in output" ] -- "null" is already taken nullFlag :: PrimDarcsOption Bool nullFlag = singleNoArg ['0'] ["null"] F.NullFlag "separate file names by NUL characters" +-- ** show repo++enumPatches :: PrimDarcsOption EnumPatches+enumPatches = withDefault YesEnumPatches+ [ RawNoArg [] ["enum-patches"] F.EnumPatches YesEnumPatches+ "include statistics requiring enumeration of patches"+ , RawNoArg [] ["no-enum-patches"] F.NoEnumPatches NoEnumPatches+ "don't include statistics requiring enumeration of patches" ]+ -- ** gzcrcs data GzcrcsAction = GzcrcsCheck | GzcrcsRepair deriving (Eq, Show)@@ -1224,11 +1351,9 @@ -- ** optimize siblings :: PrimDarcsOption [AbsolutePath]-siblings = multiAbsPathArg [] ["sibling"] F.Sibling mkV "URL" "specify a sibling directory"+siblings = multiAbsPathArg [] ["sibling"] F.Sibling mkV "DIRECTORY"+ "specify a sibling directory" where mkV fs = [ s | F.Sibling s <- fs ]--reorderPatches :: PrimDarcsOption Bool-reorderPatches = singleNoArg [] ["reorder-patches"] F.Reorder "reorder the patches in the repository" reorder :: PrimDarcsOption Reorder reorder = withDefault NoReorder
@@ -283,6 +283,9 @@ ocheck _ = [] odesc = [] +instance Semigroup (PrimOptSpec d f a [v]) where+ (<>) = oappend+ -- | See 'oappend' and 'oempty'. instance Monoid (PrimOptSpec d f a [v]) where mappend = oappend@@ -295,3 +298,13 @@ -- prop> parseFlags o fs = oparse o id fs parseFlags :: (forall a. PrimOptSpec d f a v) -> [f] -> v parseFlags o fs = oparse o id fs++-- no assoiativity, higher precedence than comparisons operators (4)+-- and lower than arithemic operators (6,7,8)+infix 5 ?++-- | Operator version of 'parseFlags'+--+-- prop> opt ? flags = parseFlags opt flags+(?) :: (forall a. PrimOptSpec d f a v) -> [f] -> v+(?) = parseFlags
@@ -16,7 +16,7 @@ | LeaveTestDir | NoLeaveTestDir | Timings | Debug | DebugHTTP | Verbose | NormalVerbosity | Quiet- | Target String | Cc String+ | To String | Cc String | Output AbsolutePathOrStd | OutputAutoName AbsolutePath | Mail | Subject String | InReplyTo String | Charset String | SendmailCmd String | Author String | SelectAuthor | PatchName String@@ -24,7 +24,7 @@ | OneHash String | AfterPatch String | UpToPatch String | AfterHash String | UpToHash String- | TagName String | LastN Int | MaxCount Int | PatchIndexRange Int Int+ | TagName String | LastN Int | MaxCount String | PatchIndexRange Int Int | NumberPatches | OneTag String | AfterTag String | UpToTag String | GenContext | Context AbsolutePath | Count@@ -95,4 +95,5 @@ | NoAmendUnrecord | AmendUnrecord | PatchIndexFlag | NoPatchIndexFlag+ | EnumPatches | NoEnumPatches deriving ( Eq, Show )
@@ -21,14 +21,17 @@ , matchSeveralOrLast , matchRange , matchSeveralOrRange- , matchAny -- temporary, for toMatchFlags+ -- * exported for for checking+ , context+ , matchLast+ , matchFrom+ , matchAny -- temporary hack ) where import Prelude () import Darcs.Prelude hiding ( last ) import Data.Char ( isDigit )-import Data.Monoid ( (<>) ) import Darcs.Patch.Match ( MatchFlag(..) ) import qualified Darcs.UI.Options.Flags as F ( DarcsFlag(..) )@@ -59,10 +62,13 @@ matchOneNontag :: MatchOption matchOneNontag = match <> patch <> hash --- | Used by: rebase pull, apply, send, push, pull, fetch+-- | Used by: rebase pull/apply, send, push, pull, apply, fetch matchSeveral :: MatchOption matchSeveral = matches <> patches <> tags <> hash +matchLast :: MatchOption+matchLast = last+ -- | Used by: rebase unsuspend/reify matchSeveralOrFirst :: MatchOption matchSeveralOrFirst = mconcat [ matchTo, last, matches, patches, tags, hash ]@@ -89,7 +95,7 @@ matchAny :: MatchOption matchAny = mconcat [ toMatch, toPatch, toHash, toTag, fromMatch, fromPatch, fromHash, fromTag,- tag, tags, patch, patches, match, matches, index, indexes, context, last ]+ tag, tags, patch, patches, hash, match, matches, index, indexes, context, last ] -- * Primitive matching options @@ -220,7 +226,7 @@ "select the last NUMBER patches" ] toInt s = if not (null s) && all isDigit s then read s else (-1) --- | TODO: see 'Darcs.UI.Options.matchMaxcount'.+-- | TODO: see 'Darcs.UI.Options.maxCount'. index = OptSpec {..} where ounparse k mfs = k [ F.PatchIndexRange n m | PatchIndexRange n m <- mfs ] oparse k fs = k [ PatchIndexRange n m | F.PatchIndexRange n m <- fs ]@@ -230,7 +236,7 @@ then F.PatchIndexRange (read s) (read s) else F.PatchIndexRange 0 0 --- | TODO: see 'Darcs.UI.Options.matchMaxcount'.+-- | TODO: see 'Darcs.UI.Options.maxCount'. indexes = OptSpec {..} where ounparse k mfs = k [ F.PatchIndexRange n m | PatchIndexRange n m <- mfs ] oparse k fs = k [ PatchIndexRange n m | F.PatchIndexRange n m <- fs ]
@@ -10,7 +10,7 @@ import Darcs.Prelude import Darcs.Patch- ( IsRepoType, RepoPatch, Patchy, PrimPatch, PrimOf, fromPrims+ ( IsRepoType, RepoPatch, PrimPatch, PrimOf, fromPrims , effect , summaryFL )@@ -27,9 +27,8 @@ import Darcs.Repository ( Repository ) import Darcs.Util.Lock- ( readLocaleFile- , writeLocaleFile- , appendToFile+ ( readTextFile+ , writeTextFile ) import Darcs.UI.External ( editFile )@@ -38,13 +37,12 @@ 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 Darcs.Util.Printer ( text, ($$), vcat, prefixLines, renderString ) import qualified Darcs.Util.Ratified as Ratified ( hGetContents ) import Darcs.Util.Tree ( Tree )@@ -53,7 +51,6 @@ 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 )@@ -83,7 +80,7 @@ -- 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)+getLog :: forall prim wX wY . PrimPatch prim => Maybe String -- ^ patchname option -> Bool -- ^ pipe option -> O.Logfile -- ^ logfile option@@ -102,7 +99,7 @@ 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 [])+ mlp <- readTextFile f `catch` (\(_ :: IOException) -> return []) firstname <- case (patchname_specified, mlp) of (FlagPatchName p, []) -> return p (_, p:_) -> if badName p@@ -165,7 +162,7 @@ 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+ writeTextFile logf $ unlines $ p : default_log append_info logf p _ <- editFile logf (name,long) <- read_long_comment logf p@@ -177,28 +174,25 @@ 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)+ do t <- readTextFile f+ let filter_out_info = filter (not.("#" `isPrefixOf`))+ case reverse $ dropWhile null $ reverse $ filter_out_info 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)+ append_info f oldname = do+ fc <- readTextFile f+ writeTextFile f $ renderString+ $ vcat (map text $ if null fc then [oldname] else fc)+ $$ 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
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.UI.PrintPatch ( printPatch , contextualPrintPatch@@ -35,17 +33,16 @@ import Darcs.Patch.Apply ( ApplyState ) import Darcs.Patch ( showContextPatch, showPatch, showNicely, description, summary )-import Darcs.Patch.Show ( ShowPatch )+import Darcs.Patch.Show ( ShowPatch, ShowContextPatch, ShowPatchFor(ForDisplay) ) import Darcs.UI.External ( viewDocWith )---import Darcs.UI.Flags ( DarcsFlag(Summary, Verbose), isUnified ) import Darcs.UI.Options.All ( Verbosity(..), Summary(..), WithContext(..) ) -import Darcs.Util.Printer ( Doc, putDocLnWith, RenderMode(..) )+import Darcs.Util.Printer ( Doc, putDocLnWith ) -- | @'printFriendly' opts patch@ prints @patch@ in accordance with the flags -- in opts, ie, whether @--verbose@ or @--summary@ were passed at the -- command-line.-printFriendly :: (ShowPatch p, ApplyState p ~ Tree) => Maybe (Tree IO)+printFriendly :: (ShowPatch p, ShowContextPatch p, ApplyState p ~ Tree) => Maybe (Tree IO) -> Verbosity -> Summary -> WithContext -> p wX wY -> IO () printFriendly (Just pristine) _ _ YesContext = contextualPrintPatch pristine printFriendly _ v s _ = putDocLnWith fancyPrinters . showFriendly v s@@ -59,16 +56,16 @@ -- | 'printPatch' prints a patch on standard output. printPatch :: ShowPatch p => p wX wY -> IO ()-printPatch p = putDocLnWith fancyPrinters $ showPatch p+printPatch p = putDocLnWith fancyPrinters $ showPatch ForDisplay p -- | 'printPatchPager' runs '$PAGER' and shows a patch in it. printPatchPager :: ShowPatch p => p wX wY -> IO ()-printPatchPager p = viewDocWith fancyPrinters Standard $ showPatch p+printPatchPager p = viewDocWith fancyPrinters $ showPatch ForDisplay p -- | 'contextualPrintPatch' prints a patch, together with its context, on -- standard output.-contextualPrintPatch :: (ShowPatch p, ApplyState p ~ Tree) => Tree IO+contextualPrintPatch :: (ShowContextPatch p, ApplyState p ~ Tree) => Tree IO -> p wX wY -> IO () contextualPrintPatch s p = do- (contextedPatch, _) <- virtualTreeIO (showContextPatch p) s+ (contextedPatch, _) <- virtualTreeIO (showContextPatch ForDisplay p) s putDocLnWith fancyPrinters contextedPatch
@@ -15,7 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-} -- | This is the actual heavy lifter code, which is responsible for parsing the -- arguments and then running the command itself. module Darcs.UI.RunCommand ( runTheCommand ) where@@ -30,15 +29,15 @@ getOpt ) import System.Exit ( ExitCode ( ExitSuccess ), exitWith ) -import Darcs.UI.Options ( DarcsOption, (^), odesc, oparse, parseFlags, optDescr )+import Darcs.UI.Options ( (^), odesc, oparse, parseFlags, optDescr, (?) ) import Darcs.UI.Options.All ( stdCmdActions, StdCmdAction(..) , anyVerbosity, verbosity, Verbosity(..), network, NetworkOptions(..)- , preHook, postHook )+ , HooksConfig(..), hooks ) import Darcs.UI.Defaults ( applyDefaults ) import Darcs.UI.External ( viewDoc )-import Darcs.UI.Flags ( DarcsFlag (NewRepo), toMatchFlags, fixRemoteRepos )+import Darcs.UI.Flags ( DarcsFlag (NewRepo), matchAny, fixRemoteRepos ) import Darcs.UI.Commands ( CommandArgs( CommandOnly, SuperCommandOnly, SuperCommandSub ) , CommandControl@@ -49,20 +48,22 @@ , commandExtraArgHelp , commandExtraArgs , commandArgdefaults- , commandGetArgPossibilities+ , commandCompleteArgs , commandOptions , commandParseOptions , wrappedCommandName , disambiguateCommands- , getCommandHelp- , getCommandMiniHelp , getSubcommands , extractCommands , superName- , subusage ) import Darcs.UI.Commands.GZCRCs ( doCRCWarnings ) import Darcs.UI.Commands.Clone ( makeRepoName, cloneToSSH )+import Darcs.UI.Usage+ ( getCommandHelp+ , getCommandMiniHelp+ , subusage+ ) import Darcs.Patch.Match ( checkMatchSyntax ) import Darcs.Repository.Prefs ( getGlobal, getPreflist )@@ -72,7 +73,6 @@ 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, quote ) @@ -101,16 +101,18 @@ -- must use the (saved) original working directory to resolve possibly -- relative paths to absolute paths. prereq_errors <- commandPrereq cmd cmdline_flags+ -- we must get the cwd again because commandPrereq has the side-effect of changing it.+ new_wd <- getCurrentDirectory user_defs <- getGlobal "defaults" repo_defs <- getPreflist "defaults" let (flags,flag_errors) = applyDefaults (fmap commandName msuper) cmd old_wd user_defs repo_defs cmdline_flags case parseFlags stdCmdActions flags of- Just Help -> viewDoc $ text $ getCommandHelp msuper cmd+ Just Help -> viewDoc $ getCommandHelp msuper cmd Just ListOptions -> do setProgressMode False- file_args <- commandGetArgPossibilities cmd- putStrLn $ intercalate "\n" $ getOptionsOptions options : file_args+ possible_args <- commandCompleteArgs cmd (new_wd, old_wd) flags orig_extra+ mapM_ putStrLn $ optionList options ++ possible_args Just Disable -> die $ "Command "++commandName cmd++" disabled with --disable option!" Nothing -> case prereq_errors of@@ -121,35 +123,31 @@ [] -> do extra <- commandArgdefaults cmd flags old_wd orig_extra case extraArgumentsError extra cmd msuper of- Nothing -> runWithHooks cmd old_wd flags extra+ Nothing -> runWithHooks cmd (new_wd, old_wd) flags extra 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) -withHookOpts :: DarcsOption a (t2 -> t3 -> t4 -> t1)- -> (t2 -> t3 -> t4 -> t -> t1) -> [DarcsFlag] -> t -> a-withHookOpts opts runHook flags path = oparse opts runHook' flags where- runHook' mcmd ask verb = runHook mcmd ask verb path- runWithHooks :: DarcsCommand pf- -> AbsolutePath -> [DarcsFlag] -> [String] -> IO ()-runWithHooks cmd old_wd flags extra = do- -- NOTE: we must get the cwd again because commandPrereq has the side-effect of changing it.- new_wd <- getCurrentDirectory- checkMatchSyntax $ toMatchFlags flags+ -> (AbsolutePath, AbsolutePath)+ -> [DarcsFlag] -> [String] -> IO ()+runWithHooks cmd (new_wd, old_wd) flags extra = do+ checkMatchSyntax $ matchAny ? flags -- set any global variables oparse (anyVerbosity ^ network) setGlobalVariables flags -- actually run the command and its hooks- preHookExitCode <- withHookOpts (preHook ^ verbosity) runPrehook flags new_wd+ let hooksCfg = parseFlags hooks flags+ let verb = parseFlags verbosity flags+ preHookExitCode <- runPrehook (pre hooksCfg) verb new_wd if preHookExitCode /= ExitSuccess then exitWith preHookExitCode else do fixedFlags <- fixRemoteRepos old_wd flags phDir <- getPosthookDir new_wd cmd fixedFlags extra let parsedFlags = commandParseOptions cmd fixedFlags commandCommand cmd (new_wd, old_wd) parsedFlags extra- postHookExitCode <- withHookOpts (postHook ^ verbosity) runPosthook flags phDir+ postHookExitCode <- runPosthook (post hooksCfg) verb phDir exitWith postHookExitCode setGlobalVariables :: Bool -> Bool -> Verbosity -> Bool -> NetworkOptions -> IO ()@@ -204,10 +202,13 @@ nthOf n (_:hs) = nthOf (n-1) hs nthOf _ [] = "UNDOCUMENTED" -getOptionsOptions :: [OptDescr DarcsFlag] -> String-getOptionsOptions = intercalate "\n" . concatMap goo- where- goo (Option _ os _ _) = map ("--"++) os+optionList :: [OptDescr DarcsFlag] -> [String]+optionList = concatMap names+ where+ names (Option sos los _ desc) =+ map (short desc) sos ++ map (long desc) los+ short d o = '-' : o : ";" ++ d+ long d o = "--" ++ o ++ ";" ++ d runRawSupercommand :: DarcsCommand pf -> [String] -> IO () runRawSupercommand super [] =@@ -219,7 +220,7 @@ -- note: we do not apply defaults here (flags,_,getopt_errs) -> case parseFlags stdCmdActions flags of Just Help ->- viewDoc $ text $ getCommandHelp Nothing super+ viewDoc $ getCommandHelp Nothing super Just ListOptions -> do putStrLn "--help" mapM_ (putStrLn . wrappedCommandName) (extractCommands $ getSubcommands super)
@@ -15,9 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}-- module Darcs.UI.SelectChanges ( -- * Working with changes WhichChanges(..)@@ -57,16 +54,16 @@ import Prelude () import Darcs.Prelude -import Prelude hiding ( (^) ) import Control.Monad ( liftM, unless, when, (>=>) ) import Control.Monad.Identity ( Identity (..) ) import Control.Monad.Reader- ( Reader, ReaderT, asks- , runReader, runReaderT+ ( ReaderT+ , asks+ , runReaderT ) import Control.Monad.State ( StateT, execStateT, gets- , modify, runStateT+ , modify, runStateT, state ) import Control.Monad.Trans ( liftIO ) import Data.List ( intercalate, union )@@ -74,41 +71,43 @@ import System.Exit ( exitSuccess ) import Darcs.Patch- ( Patchy, PrimPatch, IsRepoType, RepoPatch, PrimOf+ ( IsRepoType, RepoPatch, PrimOf , commuteFLorComplain, invert , listTouchedFiles, fromPrims ) import qualified Darcs.Patch ( thing, things, summary )-import Darcs.Patch.Apply ( ApplyState )+import Darcs.Patch.Apply ( Apply, ApplyState ) import Darcs.Patch.Choices ( PatchChoices, Slot (..), LabelledPatch- , patchChoicesLps, forceFirsts+ , mkPatchChoices, forceFirsts , forceFirst, forceLast, forceMatchingFirst , forceMatchingLast, getChoices , makeEverythingLater, makeEverythingSooner- , makeUncertain, patchChoices- , patchChoicesLpsSub, patchSlot'+ , forceMiddle, patchChoices+ , patchSlot , refineChoices, selectAllMiddles , separateFirstFromMiddleLast- , substitute, label, lpPatch+ , substitute, label, unLabel+ , labelPatches )+import Darcs.Patch.Commute ( Commute ) import Darcs.Patch.Info ( PatchInfo ) import Darcs.Patch.Inspect ( PatchInspect ) import Darcs.Patch.Invert ( Invert )-import Darcs.Patch.Match ( haveNonrangeMatch, matchAPatch, matchAPatchread )+import Darcs.Patch.Match ( haveNonrangeMatch, matchAPatch ) import Darcs.Patch.Named.Wrapped ( anonymous ) import Darcs.Patch.PatchInfoAnd ( PatchInfoAnd, info, n2pia )-import Darcs.Patch.Set ( PatchSet(..), newset2RL )-import Darcs.Patch.Show ( ShowPatch )-import Darcs.Patch.Split ( Splitter (..) )-import qualified Darcs.Patch.TouchesFiles as TouchesFiles+import Darcs.Patch.Set ( PatchSet(..), patchSet2RL )+import Darcs.Patch.Show ( ShowPatch, ShowContextPatch )+import Darcs.Patch.Split ( Splitter(applySplitter,canonizeSplit) )+import Darcs.Patch.TouchesFiles ( selectNotTouching, deselectNotTouching ) import Darcs.Patch.Type ( PatchType (..) ) import Darcs.Patch.Witnesses.Eq ( unsafeCompare ) import Darcs.Patch.Witnesses.Ordered ( (:>) (..), (:||:) (..), FL (..) , RL (..), filterFL, lengthFL, mapFL- , mapFL_FL, reverseFL, spanFL, spanFL_M- , (+<+), (+>+), reverseRL+ , mapFL_FL, spanFL, spanFL_M+ , (+>+), (+<<+), (+>>+) ) import Darcs.Patch.Witnesses.Sealed ( FlippedSeal (..), Sealed2 (..)@@ -118,7 +117,7 @@ ( FZipper (..), left, right , rightmost, toEnd, toStart )-import Darcs.Repository ( Repository, readRepo, readTentativeRepo )+import Darcs.Repository ( Repository, repoLocation, readRepo, readTentativeRepo ) import Darcs.UI.External ( editText ) import Darcs.UI.Options.All ( Verbosity(..), Summary(..)@@ -127,7 +126,8 @@ ( printFriendly, printPatch , printPatchPager, showFriendly ) import Darcs.Util.English ( Noun (..), englishNum, capitalize )-import Darcs.Util.Printer ( prefix, putDocLn )+import Darcs.Util.Printer ( prefix, putDocLn, putDocLnWith, greenText )+import Darcs.Util.Printer.Color ( fancyPrinters ) import Darcs.Util.Prompt ( PromptConfig (..), askUser, promptChar ) import Darcs.Util.Tree ( Tree ) @@ -137,32 +137,42 @@ -- of the repository), and we either want an initial segment or a -- final segment of the poset of patches. ----- @First@: ask for an initial+-- 'First': ask for an initial -- segment, first patches first (default for all pull-like commands) ----- @FirstReversed@: ask for an initial segment, last patches first+-- 'FirstReversed': ask for an initial segment, last patches first -- (used to ask about dependencies in record, and for pull-like -- commands with the @--reverse@ flag). ----- @LastReversed@: ask for a final segment, last patches first. (default+-- 'LastReversed': ask for a final segment, last patches first. (default -- for unpull-like commands, except for selecting *primitive* patches in -- rollback) ----- @Last@: ask for a final segment, first patches first. (used for selecting+-- 'Last': ask for a final segment, first patches first. (used for selecting -- primitive patches in rollback, and for unpull-like commands with the -- @--reverse@ flag+--+-- IOW: First = initial segment+-- Last = final segment+-- Reversed = start with the newest patch instead of oldest+-- As usual, terminology is not, ahem, very intuitive. data WhichChanges = Last | LastReversed | First | FirstReversed deriving (Eq, Show) --- | A @WhichChanges@ is backwards if the order in which patches are presented--- is the opposite of the order of dependencies for that operation.+-- | A 'WhichChanges' is 'backward' if the segment of patches we ask for+-- is at the opposite end of where we start to present them. backward :: WhichChanges -> Bool backward w = w == Last || w == FirstReversed +-- | A 'WhichChanges' is reversed if the order in which patches are presented+-- is latest (or newest) patch first.+reversed :: WhichChanges -> Bool+reversed w = w == LastReversed || w == FirstReversed+ -- | The type of the function we use to filter patches when @--match@ is -- given. data MatchCriterion p = MatchCriterion { mcHasNonrange :: Bool- , mcFunction :: WhichChanges -> Sealed2 p -> Bool+ , mcFunction :: forall wA wB. WhichChanges -> LabelledPatch p wA wB -> Bool } data PatchSelectionOptions = PatchSelectionOptions@@ -187,8 +197,7 @@ } -- | A 'PatchSelectionContext' for selecting 'Prim' patches.-selectionContextPrim :: PrimPatch prim- => WhichChanges+selectionContextPrim :: WhichChanges -> String -> PatchSelectionOptions -> Maybe (Splitter prim)@@ -207,8 +216,11 @@ } -- | A 'PatchSelectionContext' for selecting full patches ('PatchInfoAnd' patches)-selectionContext :: (IsRepoType rt, RepoPatch p) => WhichChanges -> String -> PatchSelectionOptions -> Maybe (Splitter (PatchInfoAnd rt p))- -> Maybe [FilePath] -> PatchSelectionContext (PatchInfoAnd rt 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@@ -240,15 +252,12 @@ } -- | The dynamic parameters for interactive selection of patches.-data InteractiveSelectionContext p wX wY = ISC { total :: Int- -- ^ total number of patches- , current :: Int- -- ^ number of already-seen patches- , lps :: FZipper (LabelledPatch p) wX wY- -- ^ the patches we offer- , choices :: PatchChoices p wX wY- -- ^ the user's choices- }+data InteractiveSelectionContext p wX wY =+ ISC { total :: Int -- ^ total number of patches+ , current :: Int -- ^ number of already-seen patches+ , lps :: FZipper (LabelledPatch p) wX wY -- ^ the patches we offer+ , choices :: PatchChoices p wX wY -- ^ the user's choices+ } type PatchSelectionM p a = ReaderT (PatchSelectionContext p) a @@ -256,9 +265,6 @@ StateT (InteractiveSelectionContext p wX wY) (PatchSelectionM p IO) a -type PatchSelection p wX wY =- PatchSelectionM p IO ((FL p :> FL p) wX wY)- -- Common match criteria -- | For commands without @--match@, 'triv' matches all patches@@ -276,56 +282,92 @@ , mcFunction = isWantedMcFunction } where- isWantedMcFunction x = unseal2 $ iw x- iw First = unseal2 (matchAPatch mflags) . extract- iw Last = unseal2 (matchAPatch mflags) . extract- iw LastReversed = unseal2 (matchAPatch mflags) . extract . invert- iw FirstReversed = unseal2 (matchAPatch mflags) . extract . invert--liftR :: Monad m => Reader r a -> ReaderT r m a-liftR = asks . runReader+ isWantedMcFunction w = unseal2 (matchAPatch mflags) . extract_reverse w . unLabel+ -- TODO inverting should not be necessary here, at least I would expect+ --+ -- prop> matchAPatch (invert x) == matchAPatch x+ extract_reverse w = if reversed w then extract . invert else extract --- | runs a 'PatchSelection' action in the given 'PatchSelectionContext'.-runSelection :: forall p wX wY . (Patchy p, PatchInspect p, ShowPatch p, ApplyState p ~ Tree)+-- | Run a 'PatchSelection' action in the given 'PatchSelectionContext'.+runSelection :: forall p wX wY . ( Invert p, Commute p, Apply p, PatchInspect p, ShowPatch p+ , ShowContextPatch p, ApplyState p ~ Tree ) => FL p wX wY -> PatchSelectionContext p -> IO ((FL p :> FL p) wX wY)-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- normal fl = sc1 fl- reversed fl = do let ifl = invert fl- choices_ <- sc1 ifl- return $ invertC choices_+runSelection ps psc = runReaderT (selection ps) psc where+ selection+ | reversed whch = fmap invert . doit . invert+ | otherwise = doit+ -- efficiency note: we should first filterUnwanted to apply matchers,+ -- as this often requires to read only metadata; then filterNotTouching+ -- applies path restrictions which needs to read patch contents+ doit =+ fmap (canonizeAfterSplitter . selectedPatches) .+ selectChanges . filterNotTouching . filterUnwanted . patchChoices -sc1 :: forall p wX wY . (Patchy p, PatchInspect p, ShowPatch p, ApplyState p ~ Tree) =>- FL p wX wY- -> PatchSelection p wX wY-sc1 fl = do ps <- liftR (patchesToConsider fl)- patchChoices_ <- realSelectChanges ps- whch <- asks whichChanges- let sps = selectedPatches whch patchChoices_- liftR (canonizeAfterSplitter sps)+ -- configuration+ whch = whichChanges psc+ fs = files psc+ os = opts psc+ crit = matchCriterion psc+ mspl = splitter psc --- | inverses the choices that have been made-invertC :: (Patchy p) => (FL p :> FL p) wX wY -> (FL p :> FL p) wY wX-invertC (a :> b) = invert b :> invert a+ -- after selecting with a splitter, the results may not be canonical+ canonizeAfterSplitter :: (FL p :> FL p) wA wB -> (FL p :> FL p) wA wB+ canonizeAfterSplitter (x :> y) =+ let canonizeIfNeeded = maybe id canonizeSplit mspl+ in canonizeIfNeeded x :> canonizeIfNeeded y --- | Shows the patch that is actually being selected the way the user--- should see it.-repr :: (Patchy p) => WhichChanges -> Sealed2 p -> Sealed2 p-repr First (Sealed2 p) = Sealed2 p-repr LastReversed (Sealed2 p) = Sealed2 (invert p)-repr Last (Sealed2 p) = Sealed2 p-repr FirstReversed (Sealed2 p) = Sealed2 (invert p)+ -- retrieve the results of patch selection+ selectedPatches :: PatchChoices p wA wB -> (FL p :> FL p) wA wB+ selectedPatches pc+ | backward whch =+ case getChoices pc of+ fc :> mc :> lc -> mapFL_FL unLabel (fc +>+ mc) :> mapFL_FL unLabel lc+ | otherwise =+ case separateFirstFromMiddleLast pc of+ xs :> ys -> mapFL_FL unLabel xs :> mapFL_FL unLabel ys + selectChanges :: PatchChoices p wA wB+ -> PatchSelectionM p IO (PatchChoices p wA wB)+ selectChanges+ | interactive os = refineChoices textSelect+ | otherwise = return . promote++ promote+ | backward whch = makeEverythingLater+ | otherwise = makeEverythingSooner+ demote+ | backward whch = makeEverythingSooner+ | otherwise = makeEverythingLater++ filterNotTouching+ | backward whch = selectNotTouching fs+ | otherwise = deselectNotTouching fs++ -- when using @--match@, remove unmatched patches+ -- not depended upon by matched patches+ filterUnwanted :: PatchChoices p wA wB -> PatchChoices p wA wB+ filterUnwanted+ | mcHasNonrange crit =+ case selectDeps os of+ NoDeps -> deselectUnwanted+ _ -> demote . selectWanted+ | otherwise = id++ selectWanted+ | backward whch = forceMatchingLast iswanted_+ | otherwise = forceMatchingFirst iswanted_+ deselectUnwanted+ | backward whch = forceMatchingFirst (not . iswanted_)+ | otherwise = forceMatchingLast (not . iswanted_)+ iswanted_ = mcFunction crit whch++ {- end of runSelection -}+ -- | The equivalent of 'runSelection' for the @darcs log@ command-viewChanges :: (Patchy p, ShowPatch p, ApplyState p ~ Tree) => PatchSelectionOptions -> [Sealed2 p] -> IO ()+viewChanges :: (ShowPatch p, ShowContextPatch p, ApplyState p ~ Tree)+ => PatchSelectionOptions -> [Sealed2 p] -> IO () viewChanges ps_opts = textView ps_opts Nothing 0 [] -- | The type of the answers to a "shall I [wiggle] that [foo]?" question@@ -352,8 +394,8 @@ keysFor = concatMap (map kp) -- | The function for selecting a patch to amend record. Read at your own risks.-withSelectedPatchFromRepo ::- forall rt p wR wU wT. (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+withSelectedPatchFromRepo+ :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => String -- name of calling command (always "amend" as of now) -> Repository rt p wR wU wT -> PatchSelectionOptions@@ -361,7 +403,7 @@ -> IO () withSelectedPatchFromRepo jn repository o job = do patchSet <- readRepo repository- sp <- wspfr jn (matchAPatchread $ matchFlags o) (newset2RL patchSet) NilFL+ sp <- wspfr jn (matchAPatch $ matchFlags o) (patchSet2RL patchSet) NilFL case sp of Just (FlippedSeal (skipped :> selected')) -> job (skipped :> selected') Nothing ->@@ -369,7 +411,10 @@ data SkippedReason = SkippedAutomatically | SkippedManually -data WithSkipped p wX wY = WithSkipped { _skippedReason :: SkippedReason, skippedPatch :: p wX wY }+data WithSkipped p wX wY = WithSkipped+ { _skippedReason :: SkippedReason+ , skippedPatch :: p wX wY+ } -- | This ensures that the selected patch commutes freely with the skipped -- patches, including pending and also that the skipped sequences has an@@ -416,7 +461,8 @@ nextPatch = wspfr jn matches pps (WithSkipped SkippedManually p:>:skipped) 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))+ -> 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@@ -437,111 +483,9 @@ defaultPrintFriendly = printFriendly Nothing NormalVerbosity NoSummary NoContext --- 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 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)- => PatchChoices p wX wY- -> PatchSelectionM p IO (PatchChoices p wX wY)-realSelectChanges autoChoices =- do- o <- asks opts- whch <- asks whichChanges- if not $ interactive o- then return $ promote whch autoChoices- else refineChoices textSelect autoChoices- where forward whch = not $ backward whch- promote whch =- if forward whch- then makeEverythingSooner- else makeEverythingLater---- | When using @--match@, remove unmatched patches not depended upon by matched--- patches.-deselectUnwanted :: forall p wX wY . Patchy p =>- PatchChoices p wX wY ->- Reader (PatchSelectionContext p) (PatchChoices p wX wY)-deselectUnwanted pc =- do- o <- asks opts- mc <- asks matchCriterion- whichch <- asks whichChanges- let iswanted_ = mcFunction mc whichch . seal2 . lpPatch- select = if forward whichch- then forceMatchingFirst iswanted_- else forceMatchingLast iswanted_- deselect = if forward whichch- then forceMatchingLast (not . iswanted_)- else forceMatchingFirst (not . iswanted_)- return $- if mcHasNonrange mc- then if selectDeps o == NoDeps- then deselect pc- else demote whichch $ select pc- else pc- where- forward whichch = not $ backward whichch- demote whichch =- if forward whichch- then makeEverythingLater- else makeEverythingSooner---- | Selects the patches matching the match criterion, and puts them first or last--- according to whch, while respecting any dependencies.-patchesToConsider :: forall p wX wY- . (Patchy p, PatchInspect p, ApplyState p ~ Tree)- => FL p wX wY- -> Reader (PatchSelectionContext p) (PatchChoices p wX wY)-patchesToConsider ps =- do- fs <- asks files- crit <- asks matchCriterion- whch <- asks whichChanges- let deselectNotTouching =- case whch of- First -> TouchesFiles.deselectNotTouching- Last -> TouchesFiles.selectNotTouching- FirstReversed -> TouchesFiles.selectNotTouching- LastReversed -> TouchesFiles.deselectNotTouching- everything = patchChoices ps- -- 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- -> (FL p :> FL p) wY wZ-selectedPatches Last pc =- case getChoices pc of- fc :> mc :> lc -> mapFL_FL lpPatch (fc +>+ mc) :> mapFL_FL lpPatch lc--selectedPatches First pc =- case separateFirstFromMiddleLast pc of- xs :> ys -> mapFL_FL lpPatch xs :> mapFL_FL lpPatch ys--selectedPatches LastReversed pc =- case separateFirstFromMiddleLast pc of- xs :> ys -> mapFL_FL lpPatch xs :> mapFL_FL lpPatch ys--selectedPatches FirstReversed pc =- case getChoices pc of- fc :> mc :> lc -> mapFL_FL lpPatch (fc +>+ mc) :> mapFL_FL lpPatch lc- -- | Runs a function on the underlying @PatchChoices@ object-liftChoices :: forall p a wX wY . Patchy p =>- StateT (PatchChoices p wX wY) Identity a- -> InteractiveSelectionM p wX wY a+liftChoices :: StateT (PatchChoices p wX wY) Identity a+ -> InteractiveSelectionM p wX wY a liftChoices act = do ch <- gets choices let (result, _) = runIdentity $ runStateT act ch@@ -549,12 +493,14 @@ return result -- | @justDone n@ notes that @n@ patches have just been processed-justDone :: Patchy p => Int -> InteractiveSelectionM p wX wY ()+justDone :: Int -> InteractiveSelectionM p wX wY () justDone n = modify $ \isc -> isc{ current = current isc + n} -- | The actual interactive selection process.-textSelect :: forall p wX wY . (Patchy p, ShowPatch p, PatchInspect p, ApplyState p ~ Tree)- => FL (LabelledPatch p) wX wY -> PatchChoices p wX wY+textSelect :: ( Commute p, Invert p, ShowPatch p, ShowContextPatch p+ , PatchInspect p, ApplyState p ~ Tree )+ => FL (LabelledPatch p) wX wY+ -> PatchChoices p wX wY -> PatchSelectionM p IO (PatchChoices p wX wY) textSelect lps' pcs = do userSelection <- execStateT (skipMundane >>@@ -570,7 +516,8 @@ unless (rightmost z) $ textSelect' -textSelect' :: (Patchy p, ShowPatch p, PatchInspect p, ApplyState p ~ Tree)+textSelect' :: ( Commute p, Invert p, ShowPatch p, ShowContextPatch p+ , PatchInspect p, ApplyState p ~ Tree ) => InteractiveSelectionM p wX wY () textSelect' = do z <- gets lps@@ -602,7 +549,8 @@ optionsQuit :: String -> Bool -> String -> [KeyPress] optionsQuit jn allowsa someThings =- [ KeyPress 'd' (jn++" selected "++someThings++", skipping all the remaining "++someThings)+ [ KeyPress 'd' (jn++" selected "++someThings+++ ", skipping all the remaining "++someThings) | allowsa ] ++ [ KeyPress 'a' (jn++" all the remaining "++someThings)@@ -632,8 +580,9 @@ , KeyPress 'd' "confirm this operation" , KeyPress 'n' ("cancel " ++ jn) ]]) -options :: forall p wX wY . (Patchy p, ShowPatch p) => Bool ->- InteractiveSelectionM p wX wY ([[KeyPress]],[[KeyPress]])+options :: (ShowPatch p)+ => Bool+ -> InteractiveSelectionM p wX wY ([[KeyPress]],[[KeyPress]]) options single = do split <- asks splitter jn <- asks jobname@@ -654,33 +603,28 @@ -- | Returns a @Sealed2@ version of the patch we are asking the user -- about.-currentPatch :: forall p wX wY . Patchy p =>- InteractiveSelectionM p wX wY- (Maybe (Sealed2 (LabelledPatch p)))+currentPatch :: InteractiveSelectionM p wX wY (Maybe (Sealed2 (LabelledPatch p))) currentPatch = do- (FZipper _ lps_todo) :: FZipper (LabelledPatch p) wX wY <- gets lps+ FZipper _ lps_todo <- gets lps case lps_todo of NilFL -> return Nothing (lp:>:_) -> return $ Just (Sealed2 lp) -- | Returns the patches we have yet to ask the user about.-todo :: forall p wX wY . Patchy p- => InteractiveSelectionM p wX wY- (FlippedSeal (FL (LabelledPatch p)) wY)+todo :: InteractiveSelectionM p wX wY (FlippedSeal (FL (LabelledPatch p)) wY) todo = do (FZipper _ lps_todo) <- gets lps return (FlippedSeal lps_todo) -- | Modify the underlying @PatchChoices@ by some function-modChoices :: forall p wX wY . Patchy p =>- (PatchChoices p wX wY -> PatchChoices p wX wY)+modifyChoices :: (PatchChoices p wX wY -> PatchChoices p wX wY) -> InteractiveSelectionM p wX wY ()-modChoices f = modify $ \isc -> isc{choices = f $ choices isc}+modifyChoices f = modify $ \isc -> isc{choices = f $ choices isc} -- | returns @Just f@ if the 'currentPatch' only modifies @f@, -- @Nothing@ otherwise.-currentFile :: forall p wX wY . (Patchy p, PatchInspect p)- => InteractiveSelectionM p wX wY (Maybe FilePath)+currentFile :: (PatchInspect p)+ => InteractiveSelectionM p wX wY (Maybe FilePath) currentFile = do c <- currentPatch return $ case c of@@ -692,18 +636,19 @@ -- | @decide True@ selects the current patch, and @decide False@ deselects -- it.-decide :: forall p wX wY wT wU . Patchy p => Bool- -> LabelledPatch p wT wU- -> InteractiveSelectionM p wX wY ()+decide :: Commute p+ => Bool+ -> LabelledPatch p wT wU+ -> InteractiveSelectionM p wX wY () decide takeOrDrop lp = do whch <- asks whichChanges if backward whch == takeOrDrop -- we go backward xor we are dropping- then modChoices $ forceLast (label lp)- else modChoices $ forceFirst (label lp)+ then modifyChoices $ forceLast (label lp)+ else modifyChoices $ forceFirst (label lp) -- | like 'decide', but for all patches touching @file@-decideWholeFile :: forall p wX wY. (Patchy p, PatchInspect p) =>- FilePath -> Bool -> InteractiveSelectionM p wX wY ()+decideWholeFile :: (Commute p, PatchInspect p)+ => FilePath -> Bool -> InteractiveSelectionM p wX wY () decideWholeFile file takeOrDrop = do FlippedSeal lps_todo <- todo@@ -712,30 +657,29 @@ mapM_ (unseal2 $ decide takeOrDrop) patches_to_skip -- | Undecide the current patch.-postponeNext :: forall p wX wY . Patchy p => InteractiveSelectionM p wX wY ()+postponeNext :: Commute p => InteractiveSelectionM p wX wY () postponeNext = do Just (Sealed2 lp) <- currentPatch- modChoices $ makeUncertain (label lp)+ modifyChoices $ forceMiddle (label lp) -- | Focus the next patch.-skipOne :: forall p wX wY . Patchy p => InteractiveSelectionM p wX wY ()+skipOne :: InteractiveSelectionM p wX wY () skipOne = modify so where so x = x{lps = right (lps x), current = current x +1} -- | Focus the previous patch.-backOne :: forall p wX wY . Patchy p => InteractiveSelectionM p wX wY ()+backOne :: InteractiveSelectionM p wX wY () backOne = modify so where so isc = isc{lps = left (lps isc), current = max (current isc-1) 0} -- | Split the current patch (presumably a hunk), and add the replace it -- with its parts.-splitCurrent :: forall p wX wY . Patchy p- => Splitter p+splitCurrent :: Splitter p -> InteractiveSelectionM p wX wY () splitCurrent s = do FZipper lps_done (lp:>:lps_todo) <- gets lps- case applySplitter s (lpPatch lp) of+ case applySplitter s (unLabel lp) of Nothing -> return () Just (text, parse) -> do@@ -743,8 +687,7 @@ case parse newText of Nothing -> return () Just ps -> do- lps_new <- liftIO . return . snd- $ patchChoicesLpsSub (Just (label lp)) ps+ lps_new <- liftIO $ return $ labelPatches (Just (label lp)) ps modify $ \isc -> isc { total = total isc + lengthFL lps_new - 1 , lps = FZipper lps_done (lps_new +>+ lps_todo)@@ -753,51 +696,50 @@ (choices isc) } +-- | Shows the patch that is actually being selected the way the user+-- should see it.+repr :: Invert p => WhichChanges -> LabelledPatch p wX wY -> Sealed2 p+repr w p+ | reversed w = Sealed2 (invert (unLabel p))+ | otherwise = Sealed2 (unLabel p)+ -- | Returns a list of the currently selected patches, in -- their original context, i.e., not commuted past unselected -- patches.-selected :: forall p wX wY. Patchy p =>- InteractiveSelectionM p wX wY [Sealed2 p]+selected :: (Commute p, Invert p) => InteractiveSelectionM p wX wY [Sealed2 p] selected = do- whichch <- asks whichChanges- c <- gets choices- (first_chs :> _ :> last_chs) <- return $ getChoices c- return $ if backward whichch- then- mapFL (repr whichch . Sealed2 . lpPatch) last_chs- else- mapFL (repr whichch . Sealed2 . lpPatch) first_chs+ w <- asks whichChanges+ chs <- gets choices+ (first_chs :> _ :> last_chs) <- return $ getChoices chs+ return $ if backward w then mapFL (repr w) last_chs else mapFL (repr w) first_chs -- | Prints the list of the selected patches. See 'selected'.-printSelected :: (Patchy p, ShowPatch p) =>+printSelected :: (Invert p, Commute p, ShowPatch p) => InteractiveSelectionM p wX wY () printSelected = do someThings <- things o <- asks opts s <- selected liftIO $ do- putStrLn $ "---- Already selected "++someThings++" ----"- mapM_ (putDocLn . unseal2 (showFriendly (verbosity o) (summary o))) s- putStrLn $ "---- end of already selected "++someThings++" ----"+ putDocLnWith fancyPrinters $ greenText $ "---- selected "++someThings++" ----"+ mapM_ (putDocLnWith fancyPrinters . unseal2 (showFriendly (verbosity o) (summary o))) s+ putDocLnWith fancyPrinters $ greenText $ "---- end of selected "++someThings++" ----" -printSummary :: forall p wX wY . ShowPatch p => p wX wY -> IO ()+printSummary :: ShowPatch p => p wX wY -> IO () printSummary = putDocLn . prefix " " . Darcs.Patch.summary -- | Skips all remaining patches.-skipAll :: forall p wX wY . Patchy p =>- InteractiveSelectionM p wX wY ()+skipAll :: InteractiveSelectionM p wX wY () skipAll = modify $ \isc -> isc {lps = toEnd $ lps isc} -backAll :: forall p wX wY . Patchy p =>- InteractiveSelectionM p wX wY ()+backAll :: InteractiveSelectionM p wX wY () backAll = modify $ \isc -> isc {lps = toStart $ lps isc ,current = 0} isSingleFile :: PatchInspect p => p wX wY -> Bool isSingleFile p = length (listTouchedFiles p) == 1 -askConfirmation :: forall p wX wY . Patchy p =>- InteractiveSelectionM p wX wY ()+askConfirmation :: InteractiveSelectionM p wX wY () askConfirmation = do jn <- asks jobname liftIO $ when (jn `elem` ["unpull", "unrecord", "obliterate"]) $ do@@ -807,21 +749,21 @@ _ -> exitSuccess -- | The singular form of the noun for items of type @p@.-thing :: (Patchy p, ShowPatch p) => InteractiveSelectionM p wX wY String+thing :: (ShowPatch p) => InteractiveSelectionM p wX wY String thing = (Darcs.Patch.thing . helper) `liftM` gets choices where helper :: PatchChoices p wA wB -> p wA wB helper = undefined -- | The plural form of the noun for items of type @p@.-things :: (Patchy p, ShowPatch p) => InteractiveSelectionM p wX wY String+things :: (ShowPatch p) => InteractiveSelectionM p wX wY String things = (Darcs.Patch.things . helper) `liftM` gets choices where helper :: PatchChoices p wA wB -> p wA wB helper = undefined -- | The question to ask about one patch.-prompt :: (Patchy p, ShowPatch p) => InteractiveSelectionM p wX wY String+prompt :: (ShowPatch p) => InteractiveSelectionM p wX wY String prompt = do jn <- asks jobname aThing <- thing@@ -831,8 +773,8 @@ ++ "(" ++ show (n+1) ++ "/" ++ show n_max ++ ") " -- | Asks the user about one patch, returns their answer.-promptUser :: forall p wX wY . (Patchy p, ShowPatch p) => Bool -> Char- -> InteractiveSelectionM p wX wY Char+promptUser :: (ShowPatch p)+ => Bool -> Char -> InteractiveSelectionM p wX wY Char promptUser single def = do thePrompt <- prompt (basicOptions,advancedOptions) <- options single@@ -844,7 +786,8 @@ } -- | Ask the user what to do with the next patch.-textSelectOne :: forall p wX wY. (Patchy p, ShowPatch p, PatchInspect p, ApplyState p ~ Tree)+textSelectOne :: ( Invert p, Commute p, ShowPatch p, ShowContextPatch p, PatchInspect p+ , ApplyState p ~ Tree ) => InteractiveSelectionM p wX wY Bool textSelectOne = do c <- currentPatch@@ -855,11 +798,11 @@ jn <- asks jobname spl <- asks splitter whichch <- asks whichChanges- let singleFile = isSingleFile (lpPatch lp)- reprCur = repr whichch (Sealed2 (lpPatch lp))+ let singleFile = isSingleFile (unLabel lp)+ reprCur = repr whichch lp (basicOptions,advancedOptions) <- options singleFile- theSlot <- liftChoices $ patchSlot' lp- let the_default = getDefault (whichch == Last || whichch == FirstReversed) theSlot+ theSlot <- liftChoices $ state $ patchSlot lp+ let the_default = getDefault (backward whichch) theSlot yorn <- promptUser singleFile the_default let nextPatch = skipMundane >> showCur case yorn of@@ -888,7 +831,7 @@ 'a' -> do askConfirmation- modChoices $ selectAllMiddles (whichch == Last || whichch == FirstReversed)+ modifyChoices $ selectAllMiddles (backward whichch) skipAll return True 'q' -> liftIO $@@ -900,15 +843,16 @@ liftIO . putStrLn $ helpFor jn basicOptions advancedOptions return False -lastQuestion :: forall p wX wY . (Patchy p, ShowPatch p, ApplyState p ~ Tree) =>- InteractiveSelectionM p wX wY Bool+lastQuestion :: (Commute p, Invert p, ShowPatch p, ShowContextPatch p, ApplyState p ~ Tree)+ => InteractiveSelectionM p wX wY Bool lastQuestion = do jn <- asks jobname theThings <-things aThing <- thing let (basicOptions, advancedOptions) = optionsLast jn aThing yorn <- liftIO . promptChar $- PromptConfig { pPrompt = "Do you want to "++capitalize jn++" these "++theThings++"?"+ PromptConfig { pPrompt = "Do you want to "++capitalize jn+++ " these "++theThings++"?" , pBasicCharacters = "yglqk" , pAdvancedCharacters = "dan" , pDefault = Just 'y'@@ -921,11 +865,12 @@ 'l' -> printSelected >> return False 'k' -> backOne >> showCur >> return False _ -> do- liftIO . putStrLn $ helpFor "this confirmation prompt" basicOptions advancedOptions+ liftIO . putStrLn $ helpFor "this confirmation prompt"+ basicOptions advancedOptions return False -- | Shows the current patch as it should be seen by the user.-showCur :: forall p wX wY . (Patchy p, ShowPatch p, ApplyState p ~ Tree)+showCur :: (Invert p, ShowPatch p, ShowContextPatch p, ApplyState p ~ Tree) => InteractiveSelectionM p wX wY () showCur = do o <- asks opts@@ -935,11 +880,11 @@ case c of Nothing -> return () Just (Sealed2 lp) -> do- let reprCur = repr whichch (Sealed2 (lpPatch lp))+ let reprCur = repr whichch lp liftIO . unseal2 (printFriendly p (verbosity o) (summary o) (withContext o)) $ reprCur -- | The interactive part of @darcs changes@-textView :: forall p . (Patchy p, ShowPatch p, ApplyState p ~ Tree)+textView :: (ShowPatch p, ShowContextPatch p, ApplyState p ~ Tree) => PatchSelectionOptions -> Maybe Int -> Int -> [Sealed2 p] -> [Sealed2 p] -> IO ()@@ -1007,7 +952,7 @@ | otherwise = Just $ length ps_done + length ps_todo -- | Skips patches we should not ask the user about-skipMundane :: (Patchy p, ShowPatch p)+skipMundane :: (Commute p, ShowPatch p) => InteractiveSelectionM p wX wY () skipMundane = do (FZipper lps_done lps_todo) <- gets lps@@ -1016,20 +961,21 @@ jn <- asks jobname whichch <- asks whichChanges (skipped :> unskipped) <- liftChoices $ spanFL_M- (patchSlot' >=> return . decided)+ (state . patchSlot >=> return . decided) lps_todo let numSkipped = lengthFL skipped when (numSkipped > 0) . liftIO $ show_skipped o jn numSkipped skipped let boringThenInteresting = if selectDeps o == AutoDeps- then spanFL (not. mcFunction crit whichch . seal2 . lpPatch)+ then spanFL (not. mcFunction crit whichch) unskipped else NilFL :> unskipped case boringThenInteresting of boring :> interesting -> do justDone $ lengthFL boring + numSkipped- modify $ \isc -> isc {lps = FZipper (lps_done +<+ reverseFL skipped +<+ reverseFL boring) interesting}+ modify $ \isc -> isc {lps = FZipper (lps_done +<<+ skipped +<<+ boring)+ interesting} where show_skipped o jn n ps = do putStrLn $ _nevermind_ jn ++ _these_ n ++ "." when (verbosity o == Verbose) $@@ -1037,9 +983,8 @@ _nevermind_ jn = "Will not ask whether to " ++ jn ++ " " _these_ n = show n ++ " already decided " ++ _elem_ n "" _elem_ n = englishNum n (Noun "patch")- showskippedpatch :: (Patchy p, ShowPatch p) => FL (LabelledPatch p) wY wT -> IO ()- showskippedpatch =- sequence_ . mapFL (printSummary . lpPatch)+ showskippedpatch :: ShowPatch p => FL (LabelledPatch p) wY wT -> IO ()+ showskippedpatch = sequence_ . mapFL (printSummary . unLabel) decided :: Slot -> Bool decided InMiddle = False@@ -1054,7 +999,7 @@ getDefault False InFirst = 'y' getDefault False InLast = 'n' -askAboutDepends :: forall rt p wR wU wT wY . (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree)+askAboutDepends :: (IsRepoType rt, RepoPatch p, ApplyState p ~ Tree) => Repository rt p wR wU wT -> FL (PrimOf p) wT wY -> PatchSelectionOptions -> [PatchInfo] -> IO [PatchInfo]@@ -1062,49 +1007,23 @@ -- ideally we'd just default the olddeps to yes but still ask about them. -- SelectChanges doesn't currently (17/12/09) offer a way to do this so would -- have to have this support added first.- pps <- readTentativeRepo repository+ pps <- readTentativeRepo repository (repoLocation repository) pa <- n2pia `fmap` anonymous (fromPrims pa') -- FIXME: this code is completely unreadable- FlippedSeal ps <- return- ((case pps of- 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+ FlippedSeal ps <-+ return $ case pps of PatchSet _ x -> FlippedSeal (x+>>+(pa:>:NilFL))+ let my_lps = labelPatches Nothing ps+ pc = mkPatchChoices my_lps+ tas =+ case catMaybes (mapFL (\lp -> if pa `unsafeCompare` unLabel lp || info (unLabel lp) `elem` olddeps then Just (label lp) else Nothing) my_lps) of [] -> error "askAboutDepends: []" tgs -> tgs- Sealed2 ps' <- return $ case getChoices (forceFirsts tas pc) of _ :> mc :> _ -> Sealed2 $ mapFL_FL lpPatch mc+ Sealed2 ps' <- return $+ case getChoices (forceFirsts tas pc) of+ _ :> mc :> _ -> Sealed2 $ mapFL_FL unLabel mc (deps:>_) <- runSelection ps' $- selectionContext FirstReversed "depend on" ps_opts { matchFlags = [], interactive = True } Nothing Nothing+ selectionContext FirstReversed "depend on" ps_opts+ { matchFlags = [], interactive = True } Nothing Nothing return $ olddeps `union` mapFL info deps-{-- where- askdep_allowed = not . patchSelectFlag- opts' = filter askdep_allowed cfg- psOpts = (recordPatchSelOpts cfg)--}--{---- | @'patchSelectFlag' f@ holds whenever @f@ is a way of selecting--- patches such as @PatchName n@. <- ???-patchSelectFlag :: F.DarcsFlag -> Bool-patchSelectFlag F.All = True-patchSelectFlag (F.PatchName _) = True -- ???-patchSelectFlag (F.OnePatch _) = True-patchSelectFlag (F.OneHash _) = True-patchSelectFlag (F.SeveralPatch _) = True-patchSelectFlag (F.AfterPatch _) = True-patchSelectFlag (F.UpToPatch _) = True-patchSelectFlag (F.TagName _) = True-patchSelectFlag (F.LastN _) = True-patchSelectFlag (F.OneTag _) = True-patchSelectFlag (F.AfterTag _) = True-patchSelectFlag (F.UpToTag _) = True-patchSelectFlag (F.OnePattern _) = True-patchSelectFlag (F.SeveralPattern _) = True-patchSelectFlag (F.AfterPattern _) = True-patchSelectFlag (F.UpToPattern _) = True-patchSelectFlag _ = False--}
@@ -15,7 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-} module Darcs.UI.TheCommands ( commandControlList ) where import Prelude ()
@@ -9,39 +9,63 @@ -- --no-recursive don't add contents of subdirectories -- @ -module Darcs.UI.Usage ( usageInfo ) where+{-# LANGUAGE OverloadedStrings #-}+module Darcs.UI.Usage+ ( usageInfo+ , formatOptions+ , getCommandHelp+ , getCommandMiniHelp+ , usage+ , subusage+ ) where import Prelude () import Darcs.Prelude import Data.Functor.Compose import System.Console.GetOpt( OptDescr(..), ArgDescr(..) )-import Darcs.UI.Options ( DarcsOptDescr )+import Darcs.UI.Options.All ( stdCmdActions )+import Darcs.UI.Commands+ ( CommandControl(..)+ , DarcsCommand(..)+ , wrappedCommandName+ , wrappedCommandDescription+ , getSubcommands+ , commandAlloptions+ )+import Darcs.UI.Options ( DarcsOptDescr, odesc )+import Darcs.Util.Printer+ ( Doc, text, vsep, ($$), vcat, hsep+ , renderString+ ) --- | Variant of 'System.Console.GetOpt.usageInfo'.--- Return a string describing the usage of a command, derived from the header--- (first argument) and the options described by the second argument.------ Sequences of long switches are presented on separate lines.-usageInfo :: String -- header- -> [DarcsOptDescr a] -- option descriptors- -> String -- nicely formatted decription of options-usageInfo header optDescr = unlines (header:table)- where (ss,ls,ds) = (unzip3 . concatMap fmtOpt) optDescr+formatOptions :: [DarcsOptDescr a] -> [String]+formatOptions optDescrs = table+ where (ss,ls,ds) = (unzip3 . concatMap fmtOpt) optDescrs table = zipWith3 paste shortPadded (zipWith (++) (map (unlines' . init) ls) (sameLen $ map last ls)) ds shortPadded = sameLen ss- prePad = replicate (4 + length (head shortPadded)) ' '+ prePad = replicate (3 + length (head shortPadded)) ' ' -- Similar to unlines (additional ',' and padding): unlines' = concatMap (\x -> x ++ ",\n" ++ prePad) -- Unchanged:- paste x y z = " " ++ x ++ " " ++ y ++ " " ++ z+ paste x y z = " " ++ x ++ " " ++ y ++ " " ++ z sameLen xs = flushLeft ((maximum . map length) xs) xs flushLeft n xs = [ take n (x ++ repeat ' ') | x <- xs ] +-- | Variant of 'System.Console.GetOpt.usageInfo'.+-- Return a string describing the usage of a command, derived from the header+-- (first argument) and the options described by the second argument.+--+-- Sequences of long switches are presented on separate lines.+usageInfo :: String -- header+ -> [DarcsOptDescr a] -- option descriptors+ -> String -- nicely formatted decription of options+usageInfo header optDescrs = unlines (header:formatOptions optDescrs)+ -- Mild variant of the standard definition: 'losFmt' is a list rather than a -- comma separated string. fmtOpt :: DarcsOptDescr a -> [(String,[String],String)]@@ -49,22 +73,137 @@ case lines descr of [] -> [(sosFmt,losFmt,"")] (d:ds) -> (sosFmt,losFmt,d) : [ ("",[],d') | d' <- ds ]- where sepBy _ [] = ""- sepBy _ [x] = x- sepBy ch (x:xs) = x ++ ch:' ':sepBy ch xs- sosFmt = sepBy ',' (map (fmtShort ad) sos)+ where endBy _ [] = ""+ endBy ch [x] = x ++ [ch]+ endBy ch (x:xs) = x ++ ch:' ':endBy ch xs+ sosFmt = endBy ',' (map fmtShort sos) losFmt = map (fmtLong ad) los -------------------------------------------------------------------------------- -- Verbatim copies: these definitions aren't exported by System.Console.GetOpt -------------------------------------------------------------------------------- -fmtShort :: ArgDescr a -> Char -> String-fmtShort (NoArg _ ) so = ['-', so]-fmtShort (ReqArg _ ad) so = "-" ++ [so] ++ " " ++ ad-fmtShort (OptArg _ ad) so = "-" ++ [so] ++ "[" ++ ad ++ "]"+fmtShort :: Char -> String+fmtShort so = "-" ++ [so] fmtLong :: ArgDescr a -> String -> String fmtLong (NoArg _ ) lo = "--" ++ lo fmtLong (ReqArg _ ad) lo = "--" ++ lo ++ "=" ++ ad fmtLong (OptArg _ ad) lo = "--" ++ lo ++ "[=" ++ ad ++ "]"+--------------------------------------------------------------------------------++usage :: [CommandControl] -> Doc+usage cs = vsep + [ "Usage: darcs COMMAND ..."+ , "Commands:" $$ usageHelper cs+ , vcat+ [ "Use 'darcs COMMAND --help' for help on a single command."+ , "Use 'darcs --version' to see the darcs version number."+ , "Use 'darcs --exact-version' to see a detailed darcs version."+ , "Use 'darcs help patterns' for help on patch matching."+ , "Use 'darcs help environment' for help on environment variables."+ , "Use 'darcs help manpage' to display help in the manpage format."+ , "Use 'darcs help markdown' to display help in the markdown format."+ ]+ , "Check bug reports at http://bugs.darcs.net/"+ ]++subusage :: DarcsCommand pf -> String+subusage super = renderString $ vsep+ [ header+ , subcommandsHelp+ , vcat $ map text $ formatOptions $ odesc stdCmdActions+ , text $ commandHelp super+ ]+ where+ usageHelp = hsep $ map text+ [ "Usage:"+ , commandProgramName super+ , commandName super+ , "SUBCOMMAND ..."+ ]+ header = usageHelp $$ text (commandDescription super)+ subcommandsHelp = case getSubcommands super of+ [] -> mempty+ subcommands -> usageHelper subcommands++usageHelper :: [CommandControl] -> Doc+usageHelper xs = vsep (groups xs)+ where+ groups [] = []+ groups (HiddenCommand _:cs) = groups cs+ groups (GroupName n:cs) =+ mempty : case groups cs of+ [] -> [text n]+ (g:gs) -> (text n $$ g) : gs+ groups (CommandData c:cs) =+ case groups cs of+ [] -> [cmdHelp c]+ (g:gs) -> (cmdHelp c $$ g) : gs++ cmdHelp c = text $ " " +++ padSpaces maxwidth (wrappedCommandName c) +++ wrappedCommandDescription c++ padSpaces n s = s ++ replicate (n - length s) ' '++ maxwidth = maximum $ 15 : (map cwidth xs)++ cwidth (CommandData c) = length (wrappedCommandName c) + 2+ cwidth _ = 0++getCommandMiniHelp :: Maybe (DarcsCommand pf1) -> DarcsCommand pf2 -> String+getCommandMiniHelp msuper cmd = renderString $ vsep+ [ getCommandHelpCore msuper cmd+ , hsep $ map text+ [ "See"+ , commandProgramName cmd+ , "help"+ , maybe "" ((++ " ") . commandName) msuper ++ commandName cmd+ , "for details."+ ]+ ]++getCommandHelp :: Maybe (DarcsCommand pf1) -> DarcsCommand pf2 -> Doc+getCommandHelp msuper cmd = vsep+ [ getCommandHelpCore msuper cmd+ , subcommandsHelp+ , withHeading "Options:" basicOptionsHelp+ , withHeading "Advanced options:" advancedOptionsHelp+ , text $ commandHelp cmd+ ]+ where+ withHeading _ [] = mempty+ withHeading h ls = vcat (text h : map text ls)++ (basic, advanced) = commandAlloptions cmd+ -- call formatOptions with combined options so that+ -- both get the same formatting+ (basicOptionsHelp, advancedOptionsHelp) =+ splitAt (length basic) $ formatOptions (basic ++ advanced)++ subcommandsHelp =+ case msuper of+ Nothing ->+ case getSubcommands cmd of+ [] -> mempty+ subcommands -> usageHelper subcommands+ -- we don't want to list subcommands if we're already specifying them+ Just _ -> mempty++getCommandHelpCore :: Maybe (DarcsCommand pf1) -> DarcsCommand pf2 -> Doc+getCommandHelpCore msuper cmd = vcat+ [ hsep $+ [ "Usage:"+ , text $ commandProgramName cmd+ , maybe mempty (text . commandName) msuper+ , text $ commandName cmd+ , "[OPTION]..."+ ]+ ++ args_help+ , text $ commandDescription cmd+ ]+ where+ args_help = case cmd of+ (DarcsCommand {}) -> map text $ commandExtraArgHelp cmd+ _ -> []
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- -- | -- Module : Darcs.Util.AtExit -- Copyright : 2005 Tomasz Zielonka
@@ -1,30 +0,0 @@--- Reporting bugs in darcs. See also impossible.h.-module Darcs.Util.Bug- ( _bug, _bugDoc, _impossible, _fromJust- ) where--import Prelude ()-import Darcs.Prelude--import Data.Maybe(fromMaybe)-import Darcs.Util.Printer ( Doc, errorDoc, text, ($$) )--type BugStuff = (String, Int, String, String)--_bug :: BugStuff -> String -> a-_bug bs s = _bugDoc bs (text s)--_bugDoc :: BugStuff -> Doc -> a-_bugDoc bs s = errorDoc $- text ("bug at " ++ _bugLoc bs) $$ s $$- text ("See http://wiki.darcs.net/BugTracker/Reporting " ++- "for help on bug reporting.")--_bugLoc :: BugStuff -> String-_bugLoc (file, line, date, time) = file++":"++show line++" compiled "++time++" "++date--_impossible :: BugStuff -> a-_impossible bs = _bug bs $ "Impossible case at "++_bugLoc bs--_fromJust :: BugStuff -> Maybe a -> a-_fromJust bs = fromMaybe (_bug bs $ "fromJust error at " ++ _bugLoc bs)
@@ -1,5 +1,4 @@-{-# LANGUAGE BangPatterns, ForeignFunctionInterface, CPP, ScopedTypeVariables #-}-+{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module : Darcs.Util.ByteString@@ -15,119 +14,85 @@ -- GZIp and MMap IO for ByteStrings, encoding utilities, and miscellaneous -- functions for Data.ByteString ----module Darcs.Util.ByteString (-- unsafeWithInternals,- unpackPSFromUTF8,- packStringToUTF8,-- -- IO with mmap or gzip- gzReadFilePS,- mmapFilePS,- gzWriteFilePS,- gzWriteFilePSs,- gzReadStdin,- gzWriteHandle,- FileSegment,- readSegment,-- -- gzip handling- isGZFile,- gzDecompress,-- -- list utilities- dropSpace,- breakSpace,- linesPS,- unlinesPS,- hashPS,- breakFirstPS,- breakLastPS,- substrPS,- readIntPS,- isFunky,- fromHex2PS,- fromPS2Hex,- betweenLinesPS,- breakAfterNthNewline,- breakBeforeNthNewline,- intercalate,-- -- encoding and unicode utilities- isAscii,- decodeLocale,- encodeLocale,- decodeString+module Darcs.Util.ByteString+ (+ -- * IO with mmap or gzip+ gzReadFilePS+ , mmapFilePS+ , gzWriteFilePS+ , gzWriteFilePSs+ , gzReadStdin+ , gzWriteHandle+ , FileSegment+ , readSegment+ -- * gzip handling+ , isGZFile+ , gzDecompress+ -- * list utilities+ , dropSpace+ , breakSpace+ , linesPS+ , unlinesPS+ , hashPS+ , breakFirstPS+ , breakLastPS+ , substrPS+ , readIntPS+ , isFunky+ , fromHex2PS+ , fromPS2Hex+ , betweenLinesPS+ , intercalate+ -- * encoding and unicode utilities+ , isAscii+ , decodeLocale+ , encodeLocale+ , unpackPSFromUTF8+ , packStringToUTF8+ -- * properties+ , prop_unlinesPS_linesPS_left_inverse+ , prop_linesPS_length+ , prop_unlinesPS_length+ , propHexConversion+ , spec_betweenLinesPS ) where import Prelude () import Darcs.Prelude +import Codec.Binary.Base16 ( b16Enc, b16Dec )+ 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 qualified Data.ByteString.Lazy as BL import Data.ByteString (intercalate)-import Data.ByteString.Internal (fromForeignPtr) -import Control.Exception ( catch, SomeException ) import System.IO ( withFile, IOMode(ReadMode) , hSeek, SeekMode(SeekFromEnd,AbsoluteSeek) , openBinaryFile, hClose, Handle, hGetChar , stdin)+import System.IO.Error ( catchIOError ) import System.IO.Unsafe ( unsafePerformIO ) -import Foreign.Storable ( peek )-import Foreign.Marshal.Array ( advancePtr )-import Foreign.C.Types ( CInt(..) )- import Data.Bits ( rotateL )-import Data.Char ( ord, isSpace )+import Data.Char ( ord, isSpace, toLower, toUpper ) import Data.Word ( Word8 ) 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 )+import Data.List ( intersperse ) import Control.Monad ( when )-#if MIN_VERSION_zlib(0,6,0) import Control.Monad.ST.Lazy ( ST )-#endif -import Foreign.Ptr ( plusPtr, Ptr )-import Foreign.ForeignPtr ( withForeignPtr )--#ifdef DEBUG_PS-import Foreign.ForeignPtr ( addForeignPtrFinalizer )-import Foreign.Ptr ( FunPtr )-#endif--import qualified Data.ByteString.Lazy as BL import qualified Codec.Compression.GZip as GZ import qualified Codec.Compression.Zlib.Internal as ZI-import Darcs.Util.Encoding ( decode, encode )+import Darcs.Util.Encoding ( decode, encode, decodeUtf8, encodeUtf8 ) import Darcs.Util.Global ( addCRCWarning ) #if mingw32_HOST_OS #else import System.IO.MMap( mmapFileByteString )-import System.Posix.Files( fileSize, getSymbolicLinkStatus ) #endif import System.Mem( performGC )-import qualified Bundled.Posix as Bundled ( getFileStatus, fileSize )---- -------------------------------------------------------------------------------- obsolete debugging code---- -------------------------------------------------------------------------------- unsafeWithInternals---- | Do something with the internals of a PackedString. Beware of--- altering the contents!-unsafeWithInternals :: B.ByteString -> (Ptr Word8 -> Int -> IO a) -> IO a-unsafeWithInternals ps f- = case BI.toForeignPtr ps of- (fp,s,l) -> withForeignPtr fp $ \p -> f (p `plusPtr` s) l+import System.Posix.Files( fileSize, getSymbolicLinkStatus ) -- | readIntPS skips any whitespace at the beginning of its argument, and -- reads an Int from the beginning of the PackedString. If there is no@@ -138,33 +103,19 @@ readIntPS :: B.ByteString -> Maybe (Int, B.ByteString) readIntPS = BC.readInt . BC.dropWhile isSpace --- -------------------------------------------------------------------------------- Destructor functions (taking PackedStrings apart)---- | Decodes a 'ByteString' containing UTF-8 to a 'String'. Decoding errors are--- flagged with the U+FFFD character.-unpackPSFromUTF8 :: B.ByteString -> String-unpackPSFromUTF8 = T.unpack . decodeUtf8With lenientDecode--packStringToUTF8 :: String -> B.ByteString-packStringToUTF8 = encodeUtf8 . T.pack- --------------------------------------------------------------------------- A reimplementation of Data.ByteString.Char8.dropSpace, but--- specialised to darcs' need for a 4 way isspace.------ TODO: if it is safe to use the expanded definition of isSpaceWord8--- provided by Data.ByteString.Char8, then all this can go.- -- A locale-independent isspace(3) so patches are interpreted the same everywhere. -- ((c) == ' ' || (c) == '\t' || (c) == '\n' || (c) == '\r') isSpaceWord8 :: Word8 -> Bool isSpaceWord8 = (`elem` [0x20, 0x09, 0x0A, 0x0D]) {-# INLINE isSpaceWord8 #-} +-- | Drop leading white space, where white space is defined as+-- consisting of ' ', '\t', '\n', or '\r'. dropSpace :: B.ByteString -> B.ByteString dropSpace bs = B.dropWhile isSpaceWord8 bs +-- | Split at first occurrence of ' ', '\t', '\n', or '\r'. breakSpace :: B.ByteString -> (B.ByteString, B.ByteString) breakSpace bs = B.break isSpaceWord8 bs @@ -172,50 +123,24 @@ {-# INLINE isFunky #-} isFunky :: B.ByteString -> Bool-isFunky ps = case BI.toForeignPtr ps of- (x,s,l) ->- unsafePerformIO $ withForeignPtr x $ \p->- (/=0) `fmap` has_funky_char (p `plusPtr` s) (fromIntegral l)--foreign import ccall unsafe "fpstring.h has_funky_char" has_funky_char- :: Ptr Word8 -> CInt -> IO CInt+isFunky ps = 0 `B.elem` ps || 26 `B.elem` ps ------------------------------------------------------------------------ --- ByteString rewrites break (=='x') to breakByte 'x'--- break ((==) x) = breakChar x--- break (==x) = breakChar x-----{--{-# INLINE breakOnPS #-}-breakOnPS :: Char -> B.ByteString -> (B.ByteString, B.ByteString)-breakOnPS c p = case BC.elemIndex c p of- Nothing -> (p, BC.empty)- Just n -> (B.take n p, B.drop n p)--}- {-# INLINE hashPS #-} hashPS :: B.ByteString -> Int32-hashPS ps =- case BI.toForeignPtr ps of- (x,s,l) ->- unsafePerformIO $ withForeignPtr x $ \p->- hash (p `plusPtr` s) l+hashPS = B.foldl' hashByte 0 -hash :: Ptr Word8 -> Int -> IO Int32-hash = f (0 :: Int32)- where f h _ 0 = return h- f h p n = do x <- peek p- let !h' = fromIntegral x + rotateL h 8- f h' (p `advancePtr` 1) (n-1)+{-# INLINE hashByte #-}+hashByte :: Int32 -> Word8 -> Int32+hashByte h x = fromIntegral x + rotateL h 8 {-# INLINE substrPS #-} substrPS :: B.ByteString -> B.ByteString -> Maybe Int substrPS tok str | B.null tok = Just 0 | B.length tok > B.length str = Nothing- | otherwise = do n <- BC.elemIndex (BC.head tok) str+ | otherwise = do n <- B.elemIndex (B.head tok) str let ttok = B.tail tok reststr = B.drop (n+1) str if ttok == B.take (B.length ttok) reststr@@ -238,49 +163,31 @@ Nothing -> Nothing Just n -> Just (B.take n p, B.drop (n+1) p) --- TODO: rename+------------------------------------------------------------------------+-- linesPS and unlinesPS+ {-# INLINE linesPS #-} linesPS :: B.ByteString -> [B.ByteString] linesPS ps | B.null ps = [B.empty] | otherwise = BC.split '\n' ps -{- QuickCheck property:--import Test.QuickCheck-import qualified Data.ByteString.Char8 as BC-import Data.Char-instance Arbitrary BC.ByteString where- arbitrary = fmap BC.pack arbitrary-instance Arbitrary Char where- arbitrary = chr `fmap` choose (32,127)-deepCheck = check (defaultConfig { configMaxTest = 10000})-testLines = deepCheck (\x -> (linesPS x == linesPSOld x))-linesPSOld ps = case BC.elemIndex '\n' ps of- Nothing -> [ps]- Just n -> B.take n ps : linesPS (B.drop (n+1) ps) -}--{-| This function acts exactly like the "Prelude" unlines function, or like-"Data.ByteString.Char8" 'unlines', but with one important difference: it will-produce a string which may not end with a newline! That is:+{-# INLINE unlinesPS #-}+unlinesPS :: [B.ByteString] -> B.ByteString+unlinesPS [] = B.empty+unlinesPS x = B.concat $ intersperse (BC.singleton '\n') x -> unlinesPS ["foo", "bar"]+-- properties of linesPS and unlinesPS -evaluates to \"foo\\nbar\", not \"foo\\nbar\\n\"! This point should hold true for-'linesPS' as well.+prop_unlinesPS_linesPS_left_inverse :: B.ByteString -> Bool+prop_unlinesPS_linesPS_left_inverse x = unlinesPS (linesPS x) == x -TODO: rename this function. -}-unlinesPS :: [B.ByteString] -> B.ByteString-unlinesPS [] = BC.empty-unlinesPS x = BC.init $ BC.unlines x-{-# INLINE unlinesPS #-}-{- QuickCheck property:+prop_linesPS_length :: B.ByteString -> Bool+prop_linesPS_length x = length (linesPS x) == length (BC.elemIndices '\n' x) + 1 -testUnlines = deepCheck (\x -> (unlinesPS x == unlinesPSOld x))-unlinesPSOld ss = BC.concat $ intersperse_newlines ss- where intersperse_newlines (a:b:s) = a : newline : intersperse_newlines (b:s)- intersperse_newlines s = s- newline = BC.pack "\n" -}+prop_unlinesPS_length :: [B.ByteString] -> Bool+prop_unlinesPS_length xs =+ B.length (unlinesPS xs) == if null xs then 0 else sum (map B.length xs) + length xs - 1 -- ----------------------------------------------------------------------------- -- gzReadFilePS@@ -293,17 +200,12 @@ gzDecompress mbufsize = -- This is what the code would be without the bad CRC recovery logic: -- return . BL.toChunks . GZ.decompressWith decompressParams-#if MIN_VERSION_zlib(0,6,0) decompressWarn (ZI.decompressST ZI.gzipFormat decompressParams)-#else- toListWarn . ZI.decompressWithErrors ZI.gzipFormat decompressParams-#endif where decompressParams = case mbufsize of Just bufsize -> GZ.defaultDecompressParams { GZ.decompressBufferSize = bufsize } Nothing -> GZ.defaultDecompressParams -#if MIN_VERSION_zlib(0,6,0) decompressWarn :: (forall s . ZI.DecompressStream (ST s)) -> BL.ByteString -> ([B.ByteString], Bool) decompressWarn = ZI.foldDecompressStreamWithInput (\x ~(xs, b) -> (x:xs, b))@@ -312,37 +214,18 @@ else error "trailing data at end of compressed stream" ) handleBad-#else- toListWarn :: ZI.DecompressStream -> ([B.ByteString], Bool)- toListWarn = foldDecompressStream (\x ~(xs, b) -> (x:xs, b)) ([], False) handleBad - -- cut and paste from Zlib since it's not currently exported (interface not yet certain)- foldDecompressStream :: (B.ByteString -> a -> a) -> a- -> (ZI.DecompressError -> String -> a)- -> ZI.DecompressStream -> a- foldDecompressStream chunk end err = fold- where- fold ZI.StreamEnd = end- fold (ZI.StreamChunk bs stream) = chunk bs (fold stream)- fold (ZI.StreamError code msg) = err code msg-#endif- -- For a while a bug in darcs caused gzip files with good data but bad CRCs to be -- produced. Trap bad CRC messages, run the specified action to report that it happened, -- but continue on the assumption that the data is valid.-#if MIN_VERSION_zlib(0,6,0) handleBad (ZI.DataFormatError "incorrect data check") = ([], True) handleBad e = error (show e)-#else- handleBad ZI.DataError "incorrect data check" = ([], True)- handleBad _ msg = error msg-#endif isGZFile :: FilePath -> IO (Maybe Int) isGZFile f = do h <- openBinaryFile f ReadMode header <- B.hGet h 2- if header /= BC.pack "\31\139"+ if header /= B.pack [31,139] then do hClose h return Nothing else do hSeek h SeekFromEnd (-4)@@ -394,7 +277,7 @@ rest <- B.hGetContents stdin let allStdin = B.concat [header,rest] return $- if header /= BC.pack "\31\139"+ if header /= B.pack [31,139] then allStdin else let decompress = fst . gzDecompress Nothing compressed = BL.fromChunks [allStdin]@@ -409,12 +292,12 @@ readSegment :: FileSegment -> IO BL.ByteString readSegment (f,range) = do bs <- tryToRead- `catch` (\(_::SomeException) -> do- size <- Bundled.fileSize `fmap` Bundled.getFileStatus f+ `catchIOError` (\_ -> do+ size <- fileSize `fmap` getSymbolicLinkStatus f if size == 0- then return BC.empty+ then return B.empty else performGC >> tryToRead)- return $ BL8.fromChunks [bs]+ return $ BL.fromChunks [bs] where tryToRead = case range of@@ -435,8 +318,7 @@ -- be written to swap. If you read many small files, mmapFilePS will be -- less memory-efficient than readFilePS, since each mmapFilePS takes up a -- separate page of memory. Also, you can run into bus errors if the file--- is modified. NOTE: as with 'readFilePS', the string representation in--- the file is assumed to be ISO-8859-1.+-- is modified. mmapFilePS :: FilePath -> IO B.ByteString #if mingw32_HOST_OS@@ -444,7 +326,7 @@ #else mmapFilePS f = mmapFileByteString f Nothing- `catch` (\(_ :: SomeException) -> do+ `catchIOError` (\_ -> do size <- fileSize `fmap` getSymbolicLinkStatus f if size == 0 then return B.empty@@ -454,97 +336,85 @@ -- ------------------------------------------------------------------------- -- fromPS2Hex -foreign import ccall unsafe "static fpstring.h conv_to_hex" conv_to_hex- :: Ptr Word8 -> Ptr Word8 -> CInt -> IO ()- fromPS2Hex :: B.ByteString -> B.ByteString-fromPS2Hex ps = case BI.toForeignPtr ps of- (x,s,l) ->- BI.unsafeCreate (2*l) $ \p -> withForeignPtr x $ \f ->- conv_to_hex p (f `plusPtr` s) $ fromIntegral l+fromPS2Hex = BC.map toLower . b16Enc -- ------------------------------------------------------------------------- -- fromHex2PS -foreign import ccall unsafe "static fpstring.h conv_from_hex" conv_from_hex- :: Ptr Word8 -> Ptr Word8 -> CInt -> IO ()- fromHex2PS :: B.ByteString -> B.ByteString-fromHex2PS ps = case BI.toForeignPtr ps of- (x,s,l) ->- BI.unsafeCreate (l `div` 2) $ \p -> withForeignPtr x $ \f ->- conv_from_hex p (f `plusPtr` s) (fromIntegral $ l `div` 2)+fromHex2PS s =+ case b16Dec $ BC.map toUpper s of+ Right (result, remaining)+ | B.null remaining -> result+ _ -> error "fromHex2PS: input is not hex encoded" +propHexConversion :: B.ByteString -> Bool+propHexConversion x = fromHex2PS (fromPS2Hex x) == x+ -- ------------------------------------------------------------------------- -- betweenLinesPS --- | betweenLinesPS returns the B.ByteString between the two lines given,+-- | Return the B.ByteString between the two lines given, -- or Nothing if they do not appear.- betweenLinesPS :: B.ByteString -> B.ByteString -> B.ByteString -> Maybe B.ByteString-betweenLinesPS start end ps- = case break (start ==) (linesPS ps) of- (_, _:rest@(bs1:_)) ->- case BI.toForeignPtr bs1 of- (ps1,s1,_) ->- case break (end ==) rest of- (_, bs2:_) -> case BI.toForeignPtr bs2 of (_,s2,_) -> Just $ fromForeignPtr ps1 s1 (s2 - s1)- _ -> Nothing- _ -> Nothing---- ---------------------------------------------------------------------------- breakAfterNthNewline--breakAfterNthNewline :: Int -> B.ByteString- -> Maybe (B.ByteString, B.ByteString)-breakAfterNthNewline 0 the_ps | B.null the_ps = Just (B.empty, B.empty)- | otherwise = Just (B.empty, the_ps)-breakAfterNthNewline n the_ps =- go n (B.elemIndices (BI.c2w '\n') the_ps)- where go 0 [] = Just (the_ps, B.empty)- go _ [] = Nothing- go 1 (i:_) = Just $ B.splitAt (i+1) the_ps- go !m (_:is) = go (m-1) is---- ---------------------------------------------------------------------------- breakBeforeNthNewline+betweenLinesPS start end ps =+ case B.breakSubstring start_line ps of+ (before_start, at_start)+ | not (B.null at_start)+ , B.null before_start || BC.last before_start == '\n' ->+ case B.breakSubstring end_line (B.drop (B.length start_line) at_start) of+ (before_end, at_end)+ | not (B.null at_end)+ , B.null before_end || BC.last before_end == '\n' -> Just before_end+ | otherwise -> Nothing+ | otherwise -> Nothing+ where+ start_line = BC.snoc start '\n'+ end_line = BC.snoc end '\n' -breakBeforeNthNewline :: Int -> B.ByteString -> (B.ByteString, B.ByteString)-breakBeforeNthNewline 0 the_ps- | B.null the_ps = (B.empty, B.empty)-breakBeforeNthNewline n the_ps =- go n (B.elemIndices (BI.c2w '\n') the_ps)- where go _ [] = (the_ps, B.empty)- go 0 (i:_) = B.splitAt i the_ps- go !m (_:is) = go (m-1) is+-- | Simpler but less efficient variant of 'betweenLinesPS'.+spec_betweenLinesPS :: B.ByteString -> B.ByteString -> B.ByteString+ -> Maybe B.ByteString+spec_betweenLinesPS start end ps =+ case break (start ==) (linesPS ps) of+ (_, _:after_start) ->+ case break (end ==) after_start of+ (before_end, _:_) ->+ Just $ BC.unlines before_end+ _ -> Nothing+ _ -> Nothing -- | Test if a ByteString is made of ascii characters isAscii :: B.ByteString -> Bool isAscii = B.all (< 128) --- | Decode a ByteString to a String according to the current locale--- unsafePerformIO in the locale function is ratified by the fact that GHC 6.12--- and above also supply locale conversion with functions with a pure type.--- Unrecognized byte sequences in the input are skipped.+-- * Encoding functions++-- Use of 'unsafePerformIO' is ratified by the fact that these+-- really are pure functions.++-- | Decode a 'ByteString' containing UTF-8 to a 'String'. Decoding errors+-- are flagged with the U+FFFD character.+unpackPSFromUTF8 :: B.ByteString -> String+unpackPSFromUTF8 = unsafePerformIO . decodeUtf8++-- | Encode a 'String' to a 'ByteString' using UTF-8.+packStringToUTF8 :: String -> B.ByteString+packStringToUTF8 = unsafePerformIO . encodeUtf8++-- | Decode a 'ByteString' to a 'String' according to the current locale,+-- using lone surrogates for un-decodable bytes. decodeLocale :: B.ByteString -> String decodeLocale = unsafePerformIO . decode --- | Encode a String to a ByteString with char8 encoding (i.e., the values of the--- characters become the values of the bytes; if a character value is greater--- than 255, its byte becomes the character value modulo 256)-encodeChar8 :: String -> B.ByteString-encodeChar8 = B.pack . map (fromIntegral . ord)---- | Encode a String to a ByteString according to the current locale+-- | Encode a 'String' to a 'ByteString' according to the current locale,+-- converting lone surrogates back to the original byte. If that+-- fails (because the locale does not support the full unicode range)+-- then encode using utf-8, assuming that the un-ecodable characters+-- come from patch meta data.+--+-- See also 'Darcs.UI.Commands.setEnvCautiously'. encodeLocale :: String -> B.ByteString-encodeLocale = unsafePerformIO . encode---- | Take a 'String' that represents byte values and re-decode it acording to--- the current locale.--- Note: we globally enforce char8 as the default encoding, see "Main" and--- "Darcs.Utils". This means we get command line args and environment variables--- as 'String's with char8 encoding, too. So we need this to convert such--- strings back to the user's encoding.-decodeString :: String -> IO String-decodeString = decode . encodeChar8+encodeLocale s = unsafePerformIO $ encode s `catchIOError` (\_ -> encodeUtf8 s)
@@ -28,28 +28,7 @@ -- the commandline can end with "%<" specifying that the command -- expects input on stdin. ----- Some tests for the parser.------ > formatTable = [('s',"<insert subject here>"),--- > ('a',"<insert author here>")]--- >--- > testParser :: (Show a, Eq a) => Parser a -> String -> a -> a--- > testParser p s ok = case parse p "" s of--- > Left e -> error $ "Parser failed with: " ++ (show e)--- > Right res -> if res == ok--- > then res--- > else error $ "Parser failed: got "--- > ++ (show res) ++ ", expected "--- > ++ (show ok)--- >--- > testCases = [("a b",(["a","b"], False)),--- > ("a b %<",(["a","b"], True)),--- > ("a b %< ",(["a","b"], True)),--- > ("\"arg0 contains spaces \\\"quotes\\\"\" b",--- > (["arg0 contains spaces \"quotes\"","b"],False)),--- > ("a %s %<",(["a","<insert subject here>"], True))]--- >--- > runTests = map (uncurry $ testParser (commandline formatTable)) testCases+-- See Darcs.Test.Misc.CommandLine for tests. module Darcs.Util.CommandLine ( parseCmd
@@ -1,225 +0,0 @@--- Copyright (C) 2001, 2004 Ian Lynagh <igloo@earth.li>------ 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.---- name shadowing disabled because a,b,c,d,e are shadowed loads in step 4-{-# OPTIONS_GHC -fno-warn-name-shadowing #-}--{-# LANGUAGE CPP #-}---- |--- Module : Darcs.Util.Crypt.SHA1--- Copyright : 2001, 2004 Ian Lynagh <igloo@earth.li>--- License : GPL--- Maintainer : darcs-devel@darcs.net--- Stability : experimental--- Portability : portable--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(..) )-import Data.Char (intToDigit)-import Data.Bits (xor, (.&.), (.|.), complement, rotateL, shiftL, shiftR)-import Data.Word (Word8, Word32)-import Foreign.Ptr (Ptr, castPtr)-import Foreign.Marshal.Array (advancePtr)-import Foreign.Storable (peek, poke)-import System.IO.Unsafe (unsafePerformIO)--data SHA1 = SHA1 !Word32 !Word32 !Word32 !Word32 !Word32- deriving (Eq,Ord)-data XYZ = XYZ !Word32 !Word32 !Word32--instance Show SHA1 where- show (SHA1 a b c d e) = concatMap showAsHex [a, b, c, d, e]--instance Binary SHA1 where- put (SHA1 a b c d e) = put a >> put b >> put c >> put d >> put e- get = do a <- get ; b <- get ; c <- get ; d <- get ; e <- get ; return (SHA1 a b c d e)--sha1Xor :: SHA1 -> SHA1 -> SHA1-sha1Xor (SHA1 a1 b1 c1 d1 e1) (SHA1 a2 b2 c2 d2 e2) =- SHA1 (a1 `xor` a2) (b1 `xor` b2) (c1 `xor` c2) (d1 `xor` d2) (e1 `xor` e2)--zero :: SHA1-zero = SHA1 0 0 0 0 0--sha1PS:: B.ByteString -> SHA1-sha1PS s = abcde'- where s1_2 = sha1Step12PadLength s- abcde = sha1Step3Init- abcde' = unsafePerformIO- $ unsafeWithInternals s1_2 (\ptr len ->- do let ptr' = castPtr ptr-#ifndef BIGENDIAN- fiddleEndianness ptr' len-#endif- sha1Step4Main abcde ptr' len)--fiddleEndianness :: Ptr Word32 -> Int -> IO ()-fiddleEndianness p 0 = p `seq` return ()-fiddleEndianness p n- = do x <- peek p- poke p $ shiftL x 24- .|. shiftL (x .&. 0xff00) 8- .|. (shiftR x 8 .&. 0xff00)- .|. shiftR x 24- fiddleEndianness (p `advancePtr` 1) (n - 4)---- sha1Step12PadLength assumes the length is at most 2^61.--- This seems reasonable as the Int used to represent it is normally 32bit,--- but obviously could go wrong with large inputs on 64bit machines.--- The B.ByteString library should probably move to Word64s if this is an--- issue, though.--sha1Step12PadLength :: B.ByteString -> B.ByteString-sha1Step12PadLength s- = let len = B.length s- num_nuls = (55 - len) `mod` 64- padding = 128:replicate num_nuls 0- len_w8s = reverse $ sizeSplit 8 (fromIntegral len*8)- in B.concat [s, B.pack padding, B.pack len_w8s]--sizeSplit :: Int -> Integer -> [Word8]-sizeSplit 0 _ = []-sizeSplit p n = fromIntegral d:sizeSplit (p-1) n'- where (n', d) = divMod n 256--sha1Step3Init :: SHA1-sha1Step3Init = SHA1 0x67452301 0xefcdab89 0x98badcfe 0x10325476 0xc3d2e1f0--sha1Step4Main :: SHA1 -> Ptr Word32 -> Int -> IO SHA1-sha1Step4Main abcde _ 0 = return $! abcde-sha1Step4Main (SHA1 a0@a b0@b c0@c d0@d e0@e) s len- = do- (e, b) <- doit f1 0x5a827999 (x 0) a b c d e- (d, a) <- doit f1 0x5a827999 (x 1) e a b c d- (c, e) <- doit f1 0x5a827999 (x 2) d e a b c- (b, d) <- doit f1 0x5a827999 (x 3) c d e a b- (a, c) <- doit f1 0x5a827999 (x 4) b c d e a- (e, b) <- doit f1 0x5a827999 (x 5) a b c d e- (d, a) <- doit f1 0x5a827999 (x 6) e a b c d- (c, e) <- doit f1 0x5a827999 (x 7) d e a b c- (b, d) <- doit f1 0x5a827999 (x 8) c d e a b- (a, c) <- doit f1 0x5a827999 (x 9) b c d e a- (e, b) <- doit f1 0x5a827999 (x 10) a b c d e- (d, a) <- doit f1 0x5a827999 (x 11) e a b c d- (c, e) <- doit f1 0x5a827999 (x 12) d e a b c- (b, d) <- doit f1 0x5a827999 (x 13) c d e a b- (a, c) <- doit f1 0x5a827999 (x 14) b c d e a- (e, b) <- doit f1 0x5a827999 (x 15) a b c d e- (d, a) <- doit f1 0x5a827999 (m 16) e a b c d- (c, e) <- doit f1 0x5a827999 (m 17) d e a b c- (b, d) <- doit f1 0x5a827999 (m 18) c d e a b- (a, c) <- doit f1 0x5a827999 (m 19) b c d e a- (e, b) <- doit f2 0x6ed9eba1 (m 20) a b c d e- (d, a) <- doit f2 0x6ed9eba1 (m 21) e a b c d- (c, e) <- doit f2 0x6ed9eba1 (m 22) d e a b c- (b, d) <- doit f2 0x6ed9eba1 (m 23) c d e a b- (a, c) <- doit f2 0x6ed9eba1 (m 24) b c d e a- (e, b) <- doit f2 0x6ed9eba1 (m 25) a b c d e- (d, a) <- doit f2 0x6ed9eba1 (m 26) e a b c d- (c, e) <- doit f2 0x6ed9eba1 (m 27) d e a b c- (b, d) <- doit f2 0x6ed9eba1 (m 28) c d e a b- (a, c) <- doit f2 0x6ed9eba1 (m 29) b c d e a- (e, b) <- doit f2 0x6ed9eba1 (m 30) a b c d e- (d, a) <- doit f2 0x6ed9eba1 (m 31) e a b c d- (c, e) <- doit f2 0x6ed9eba1 (m 32) d e a b c- (b, d) <- doit f2 0x6ed9eba1 (m 33) c d e a b- (a, c) <- doit f2 0x6ed9eba1 (m 34) b c d e a- (e, b) <- doit f2 0x6ed9eba1 (m 35) a b c d e- (d, a) <- doit f2 0x6ed9eba1 (m 36) e a b c d- (c, e) <- doit f2 0x6ed9eba1 (m 37) d e a b c- (b, d) <- doit f2 0x6ed9eba1 (m 38) c d e a b- (a, c) <- doit f2 0x6ed9eba1 (m 39) b c d e a- (e, b) <- doit f3 0x8f1bbcdc (m 40) a b c d e- (d, a) <- doit f3 0x8f1bbcdc (m 41) e a b c d- (c, e) <- doit f3 0x8f1bbcdc (m 42) d e a b c- (b, d) <- doit f3 0x8f1bbcdc (m 43) c d e a b- (a, c) <- doit f3 0x8f1bbcdc (m 44) b c d e a- (e, b) <- doit f3 0x8f1bbcdc (m 45) a b c d e- (d, a) <- doit f3 0x8f1bbcdc (m 46) e a b c d- (c, e) <- doit f3 0x8f1bbcdc (m 47) d e a b c- (b, d) <- doit f3 0x8f1bbcdc (m 48) c d e a b- (a, c) <- doit f3 0x8f1bbcdc (m 49) b c d e a- (e, b) <- doit f3 0x8f1bbcdc (m 50) a b c d e- (d, a) <- doit f3 0x8f1bbcdc (m 51) e a b c d- (c, e) <- doit f3 0x8f1bbcdc (m 52) d e a b c- (b, d) <- doit f3 0x8f1bbcdc (m 53) c d e a b- (a, c) <- doit f3 0x8f1bbcdc (m 54) b c d e a- (e, b) <- doit f3 0x8f1bbcdc (m 55) a b c d e- (d, a) <- doit f3 0x8f1bbcdc (m 56) e a b c d- (c, e) <- doit f3 0x8f1bbcdc (m 57) d e a b c- (b, d) <- doit f3 0x8f1bbcdc (m 58) c d e a b- (a, c) <- doit f3 0x8f1bbcdc (m 59) b c d e a- (e, b) <- doit f2 0xca62c1d6 (m 60) a b c d e- (d, a) <- doit f2 0xca62c1d6 (m 61) e a b c d- (c, e) <- doit f2 0xca62c1d6 (m 62) d e a b c- (b, d) <- doit f2 0xca62c1d6 (m 63) c d e a b- (a, c) <- doit f2 0xca62c1d6 (m 64) b c d e a- (e, b) <- doit f2 0xca62c1d6 (m 65) a b c d e- (d, a) <- doit f2 0xca62c1d6 (m 66) e a b c d- (c, e) <- doit f2 0xca62c1d6 (m 67) d e a b c- (b, d) <- doit f2 0xca62c1d6 (m 68) c d e a b- (a, c) <- doit f2 0xca62c1d6 (m 69) b c d e a- (e, b) <- doit f2 0xca62c1d6 (m 70) a b c d e- (d, a) <- doit f2 0xca62c1d6 (m 71) e a b c d- (c, e) <- doit f2 0xca62c1d6 (m 72) d e a b c- (b, d) <- doit f2 0xca62c1d6 (m 73) c d e a b- (a, c) <- doit f2 0xca62c1d6 (m 74) b c d e a- (e, b) <- doit f2 0xca62c1d6 (m 75) a b c d e- (d, a) <- doit f2 0xca62c1d6 (m 76) e a b c d- (c, e) <- doit f2 0xca62c1d6 (m 77) d e a b c- (b, d) <- doit f2 0xca62c1d6 (m 78) c d e a b- (a, c) <- doit f2 0xca62c1d6 (m 79) b c d e a- let abcde' = SHA1 (a0 + a) (b0 + b) (c0 + c) (d0 + d) (e0 + e)- sha1Step4Main abcde' (s `advancePtr` 16) (len - 64)- where {-# INLINE f1 #-}- f1 (XYZ x y z) = (x .&. y) .|. (complement x .&. z)- {-# INLINE f2 #-}- f2 (XYZ x y z) = x `xor` y `xor` z- {-# INLINE f3 #-}- f3 (XYZ x y z) = (x .&. y) .|. (x .&. z) .|. (y .&. z)- {-# INLINE x #-}- x n = peek (s `advancePtr` n)- {-# INLINE m #-}- m n = do let base = s `advancePtr` (n .&. 15)- x0 <- peek base- x1 <- peek (s `advancePtr` ((n - 14) .&. 15))- x2 <- peek (s `advancePtr` ((n - 8) .&. 15))- x3 <- peek (s `advancePtr` ((n - 3) .&. 15))- let res = rotateL (x0 `xor` x1 `xor` x2 `xor` x3) 1- poke base res- return res- {-# INLINE doit #-}- doit f k i a b c d e = a `seq` c `seq`- do i' <- i- return (rotateL a 5 + f (XYZ b c d) + e + i' + k,- rotateL b 30)--showAsHex :: Word32 -> String-showAsHex n = showIt 8 n ""- where- showIt :: Int -> Word32 -> String -> String- showIt 0 _ r = r- showIt i x r = case quotRem x 16 of- (y, z) -> let c = intToDigit (fromIntegral z)- in c `seq` showIt (i-1) y (c:r)
@@ -1,12 +0,0 @@-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 )-import Data.ByteString.Char8 ( unpack )--sha256sum :: ByteString -> String-sha256sum = unpack . encode . hash
@@ -31,6 +31,9 @@ -- for debugging only , DateMatcher(..) , getMatchers+ -- for testing (GHCi, etc)+ , testDate+ , testDateAt ) where import Prelude ()@@ -47,7 +50,7 @@ ( parseDate, englishDateTime, englishInterval, englishLast , iso8601Interval, resetCalendar, subtractFromMCal, getLocalTz , MCalendarTime(..), toMCalendarTime, unsafeToCalendarTime- , unsetTime+ , unsetTime, readUTCDate ) -- | 'withinDay' @x y@ is true if @x <= y < (x + one_day)@@@ -179,6 +182,12 @@ tillEof p = do { x <- p; eof; return x } parseDateWith p = parse (tillEof p) "" d ++--- The following functions are for toying around in GHCi+---+--- > testDate "2008/05/22 10:34"+--- > testDateAt "2006-03-22 09:36" "2008/05/22 10:34"+ -- | 'tryMatchers' @ms@ returns the first successful match in @ms@ -- It is an error if there are no matches tryMatchers :: [DateMatcher] -> CalendarTime -> Bool@@ -190,3 +199,32 @@ now :: IO CalendarTime now = getClockTime >>= toCalendarTime++-- | 'testDate' @d@ shows the possible interpretations+-- for the date string @d@ and how they match against+-- the current date+testDate :: String -> IO ()+testDate d = do cnow <- now+ testDateAtCal cnow d++-- | 'testDate' @iso d@ shows the possible interpretations+-- for the date string @d@ and how they match against+-- the date represented by the ISO 8601 string @iso@+testDateAt :: String -> String -> IO ()+testDateAt iso = testDateAtCal (readUTCDate iso)++-- | helper function for 'testDate' and 'testDateAt'+testDateAtCal :: CalendarTime -> String -> IO ()+testDateAtCal c d =+ do ms <- getMatchers d+ putStr . unlines . map (showMatcher c) $ ms++-- | 'showMatcher' @c dm@ tells us if @dm@ applies to+-- 'CalendarTime' @c@; or if @dm@ just represents the+-- failure to parse a date, in which case @c@ is moot.+showMatcher :: CalendarTime -> DateMatcher -> String+showMatcher cnow (DM n p m) =+ "==== " ++ n ++ " ====\n" +++ (case p of+ Left err -> shows err ""+ Right x -> show x ++ "\n" ++ show (m x cnow))
@@ -2,7 +2,6 @@ -- -- BSD3 -{-# LANGUAGE CPP #-} module Darcs.Util.DateTime ( getCurrentTime, toSeconds , formatDateTime, fromClockTime, parseDateTime, startOfTime
@@ -16,8 +16,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- -- | -- Module : Darcs.Util.Diff.Myers -- Copyright : 2003 David Roundy@@ -72,8 +70,6 @@ import Data.Array.Base import Data.Array.Unboxed import qualified Data.Map as Map ( lookup, empty, insertWith )--#include "impossible.h" -- | create a list of changes between a and b, each change has the form -- (starta, lima, startb, limb) which means that a[starta, lima)
@@ -1,5 +1,3 @@-{-# LANGUAGE CPP #-}- -- Copyright (C) 2002,2008-2009 David Roundy -- -- This program is free software; you can redistribute it and/or modify@@ -34,8 +32,6 @@ ( Map, lookup, insertWith, empty, elems ) import qualified Data.Hashable as H ( hash ) import Darcs.Util.Diff.Myers (initP, aLen, PArray, getSlice)--#include "impossible.h" empty :: HunkMap empty = HunkMapInfo 0 M.empty
@@ -1,4 +1,3 @@-{-# OPTIONS_GHC -fno-warn-deprecations #-} -- using isEmptyChan {-# LANGUAGE CPP #-} -- |@@ -27,12 +26,13 @@ import Control.Arrow ( (&&&) ) import Control.Concurrent ( forkIO )-import Control.Concurrent.Chan ( isEmptyChan, newChan, readChan, writeChan,- Chan )+import Control.Concurrent.STM.TChan+ ( isEmptyTChan, newTChanIO, readTChan, writeTChan, TChan ) import Control.Concurrent.MVar ( isEmptyMVar, modifyMVar_, modifyMVar, newEmptyMVar, newMVar, putMVar, readMVar, withMVar, MVar ) import Control.Monad ( unless, when ) import Control.Monad.State ( evalStateT, get, modify, put, StateT )+import Control.Monad.STM ( atomically ) import Control.Monad.Trans ( liftIO ) import Data.IORef ( newIORef, readIORef, writeIORef, IORef ) import Data.Map ( Map )@@ -55,8 +55,6 @@ import qualified Darcs.Util.Download.HTTP as HTTP #endif -#include "impossible.h"- {-# NOINLINE maxPipelineLengthRef #-} maxPipelineLengthRef :: IORef Int maxPipelineLengthRef = unsafePerformIO $ do@@ -76,15 +74,15 @@ urlNotifications = unsafePerformIO $ newMVar Map.empty {-# NOINLINE urlChan #-}-urlChan :: Chan UrlRequest+urlChan :: TChan UrlRequest urlChan = unsafePerformIO $ do- ch <- newChan+ ch <- newTChanIO _ <- forkIO (urlThread ch) return ch type UrlM a = StateT UrlState IO a -urlThread :: Chan UrlRequest -> IO ()+urlThread :: TChan UrlRequest -> IO () urlThread ch = do junk <- flip showHex "" `fmap` randomRIO rrange evalStateT urlThread' (UrlState Map.empty emptyQ 0 junk)@@ -93,7 +91,7 @@ urlThread' :: UrlM () urlThread' = do- empty <- liftIO $ isEmptyChan ch+ empty <- liftIO $ atomically $ isEmptyTChan ch (l, w) <- (pipeLength &&& waitToStart) `fmap` get -- If we've got UrlRequests waiting on the chan, or there's nothing -- waiting to start and nothing already downloading, we just block@@ -108,10 +106,10 @@ readAllRequests :: IO [UrlRequest] readAllRequests = do- r <- readChan ch+ r <- atomically $ readTChan ch debugMessage $ "URL.urlThread (" ++ url r ++ "\n"++ "-> " ++ file r ++ ")"- empty <- isEmptyChan ch+ empty <- atomically $ isEmptyTChan ch reqs <- if not empty then readAllRequests else return []@@ -205,7 +203,7 @@ v <- newEmptyMVar old_mv <- modifyMVar urlNotifications (return . swap . Map.insertLookupWithKey (\_k _n old -> old) u v) case old_mv of- Nothing -> writeChan urlChan $ UrlRequest u f c p -- ok, new URL+ Nothing -> atomically $ writeTChan urlChan $ UrlRequest u f c p -- ok, new URL Just _ -> debugMessage $ "URL.copyUrlWithPriority already in progress, skip (" ++ u ++ "\n" ++ "-> " ++ f ++ ")" createDownloadFileName :: FilePath -> UrlState -> FilePath
@@ -23,27 +23,47 @@ -- USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. {-# LANGUAGE CPP #-} module Darcs.Util.Encoding- ( encode,decode+ ( encode, decode+ , encodeUtf8, decodeUtf8 ) where import Prelude () import Darcs.Prelude -import Data.ByteString ( ByteString )+import qualified Data.ByteString as B+import GHC.IO.Encoding ( TextEncoding, mkTextEncoding )+import GHC.Foreign ( withCStringLen, peekCStringLen ) #ifdef WIN32-import qualified Darcs.Util.Encoding.Win32 as Backend ( encode, decode )+import Darcs.Util.Encoding.Win32 ( encode, decode ) #else-import qualified Darcs.Util.Encoding.IConv as Backend ( encode, decode )++import GHC.IO.Encoding ( getFileSystemEncoding )++-- | Encode a 'String' into a 'B.ByteString' according to the user's locale+-- with the ghc specific //ROUNDTRIP feature added. This means the argument+-- is allowed to contain non-Unicode 'Char's as produced by 'decode'.+encode :: String -> IO B.ByteString+encode s = getFileSystemEncoding >>= textEncode s++-- | Decode a 'B.ByteString' into a 'String' according to the user's locale+-- with the ghc specific //ROUNDTRIP feature added. This means the result+-- may contain 'Char's that are not valid Unicode in case decoding with the+-- user's locale fails.+decode :: B.ByteString -> IO String+decode bs = getFileSystemEncoding >>= textDecode bs+ #endif --- functions redefined to add haddock (there might well be a better way!)+encodeUtf8 :: String -> IO B.ByteString+encodeUtf8 s = mkTextEncoding "UTF-8//TRANSLIT" >>= textEncode s --- | Encode a Unicode 'String' into a 'ByteString' suitable for the current--- console.-encode :: String -> IO ByteString-encode = Backend.encode+decodeUtf8 :: B.ByteString -> IO String+decodeUtf8 bs = mkTextEncoding "UTF-8//TRANSLIT" >>= textDecode bs --- | Convert a 'ByteString' from the console's encoding into a Unicode 'String'.-decode :: ByteString -> IO String-decode = Backend.decode+textEncode :: String -> TextEncoding -> IO B.ByteString+textEncode s enc = withCStringLen enc s B.packCStringLen++textDecode :: B.ByteString -> TextEncoding -> IO String+textDecode bs enc = B.useAsCStringLen bs (peekCStringLen enc)+
@@ -1,185 +0,0 @@--- Copyright 2007-2009, Judah Jacobson.--- All Rights Reserved.---- Redistribution and use in source and binary forms, with or without--- modification, are permitted provided that the following conditions are met:---- - Redistribution of source code must retain the above copyright notice,--- this list of conditions and the following disclaimer.---- - Redistribution 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 AUTHOR 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 AUTHOR OR THE 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.-{-# LANGUAGE ForeignFunctionInterface #-}-module Darcs.Util.Encoding.IConv- ( encode, decode- ) where--import Foreign.C- ( CString, CSize(..), CInt(..)- , peekCAString, withCAString- , Errno(..), getErrno, throwErrno, eINVAL, e2BIG- )-import Foreign- ( Ptr, castPtr, nullPtr, plusPtr- , peek, maybePeek- , with, maybeWith- , ForeignPtr, withForeignPtr, newForeignPtr- , FunPtr- , #type nl_item- , Word8- )-import Control.Exception ( bracket )-import Data.ByteString ( ByteString, useAsCStringLen, append )-import Data.ByteString.Internal ( createAndTrim' )-import qualified Data.ByteString as B-import qualified Data.ByteString.UTF8 as UTF8-import Data.Maybe ( fromMaybe )--#include <locale.h>-#include <langinfo.h>-#include "h_iconv.h"--getLocaleCodeset :: IO String-getLocaleCodeset = bracket (setLocale (Just "")) setLocale (const getCodeset)--encode :: String -> IO ByteString-encode str = getLocaleCodeset >>= \codeset -> openEncoder codeset >>= ($ str)--decode :: ByteString -> IO String-decode str = getLocaleCodeset >>= \codeset -> openDecoder codeset >>= ($ str)--openEncoder :: String -> IO (String -> IO ByteString)-openEncoder codeset = do- encodeT <- iconvOpen codeset "UTF-8"- return $ simpleIConv dropUTF8Char encodeT . UTF8.fromString--openDecoder :: String -> IO (ByteString -> IO String)-openDecoder codeset = do- decodeT <- iconvOpen "UTF-8" codeset- return $ fmap UTF8.toString . simpleIConv (B.drop 1) decodeT--dropUTF8Char :: ByteString -> ByteString-dropUTF8Char = fromMaybe B.empty . fmap snd . UTF8.uncons--replacement :: Word8-replacement = toEnum (fromEnum '?')---- handle errors by dropping unuseable chars.-simpleIConv :: (ByteString -> ByteString) -> IConvT -> ByteString -> IO ByteString-simpleIConv dropper t bs = do- (cs,result) <- iconv t bs- case result of- Invalid rest -> continueOnError cs rest- Incomplete rest -> continueOnError cs rest- _ -> return cs- where- continueOnError cs rest = fmap ((cs `append`) . (replacement `B.cons`))- $ simpleIConv dropper t (dropper rest)-------------------------- Setting the locale--foreign import ccall "setlocale" c_setlocale :: CInt -> CString -> IO CString--setLocale :: Maybe String -> IO (Maybe String)-setLocale oldLocale = (maybeWith withCAString) oldLocale $ \loc_p -> do- c_setlocale (#const LC_CTYPE) loc_p >>= maybePeek peekCAString---------------------- Getting the encoding--type NLItem = #type nl_item--foreign import ccall nl_langinfo :: NLItem -> IO CString--getCodeset :: IO String-getCodeset = do- str <- nl_langinfo (#const CODESET) >>= peekCAString- -- check for codesets which may be returned by Solaris, but not understood- -- by GNU iconv.- if str `elem` ["","646"]- then return "ISO-8859-1"- else return str--------------------- Iconv---- TODO: This may not work on platforms where iconv_t is not a pointer.-type IConvT = ForeignPtr ()-type IConvTPtr = Ptr ()--foreign import ccall "darcs_iconv_open" iconv_open- :: CString -> CString -> IO IConvTPtr--iconvOpen :: String -> String -> IO IConvT-iconvOpen destName srcName = withCAString destName $ \dest ->- withCAString srcName $ \src -> do- res <- iconv_open dest src- if res == nullPtr `plusPtr` (-1)- then throwErrno $ "iconvOpen "- ++ show (srcName,destName)- -- list the two it couldn't convert between?- else newForeignPtr iconv_close res---- really this returns a CInt, but it's easiest to just ignore that, I think.-foreign import ccall "& darcs_iconv_close" iconv_close :: FunPtr (IConvTPtr -> IO ())--foreign import ccall "darcs_iconv" c_iconv :: IConvTPtr -> Ptr CString -> Ptr CSize- -> Ptr CString -> Ptr CSize -> IO CSize--data Result = Successful- | Invalid ByteString- | Incomplete ByteString- deriving Show--iconv :: IConvT -> ByteString -> IO (ByteString,Result)-iconv cd inStr = useAsCStringLen inStr $ \(inPtr, inBuffLen) ->- with inPtr $ \inBuff ->- with (toEnum inBuffLen) $ \inBytesLeft -> do- out <- loop inBuffLen (castPtr inBuff) inBytesLeft- return out- where- -- TODO: maybe a better algorithm for increasing the buffer size?- -- and also maybe a different starting buffer size?- biggerBuffer = (+1)- loop outSize inBuff inBytesLeft = do- (bs, errno) <- partialIconv cd outSize inBuff inBytesLeft- inLeft <- fmap fromEnum $ peek inBytesLeft- let rest = B.drop (B.length inStr - inLeft) inStr- case errno of- Nothing -> return (bs,Successful)- Just err - | err == e2BIG -> do -- output buffer too small- (bs',result) <- loop (biggerBuffer outSize) inBuff inBytesLeft- -- TODO: is this efficient enough?- return (bs `append` bs', result)- | err == eINVAL -> return (bs,Incomplete rest)- | otherwise -> return (bs, Invalid rest)--partialIconv :: IConvT -> Int -> Ptr CString -> Ptr CSize -> IO (ByteString, Maybe Errno)-partialIconv cd outSize inBuff inBytesLeft =- withForeignPtr cd $ \cd_p ->- createAndTrim' outSize $ \outPtr ->- with outPtr $ \outBuff ->- with (toEnum outSize) $ \outBytesLeft -> do- -- ignore the return value; checking the errno is more reliable.- _ <- c_iconv cd_p inBuff inBytesLeft (castPtr outBuff) outBytesLeft- outLeft <- fmap fromEnum $ peek outBytesLeft- inLeft <- peek inBytesLeft- errno <- if inLeft > 0- then fmap Just getErrno- else return Nothing- return (0,outSize - outLeft,errno)-
@@ -39,9 +39,12 @@ ( CodePage, nullPtr, getConsoleCP, getACP , LPCSTR, LPWSTR, LPCWSTR, LPBOOL, DWORD ) +-- | Encode a Unicode 'String' into a 'ByteString' suitable for the current+-- console. encode :: String -> IO B.ByteString encode str = getCodePage >>= flip unicodeToCodePage str +-- | Convert a 'ByteString' from the console's encoding into a Unicode 'String'. decode :: B.ByteString -> IO String decode str = getCodePage >>= flip codePageToUnicode str
@@ -1,15 +0,0 @@-module Darcs.Util.Environment- (- maybeGetEnv- ) where--import Prelude ()-import Darcs.Prelude--import System.Environment ( getEnv )--import Darcs.Util.Exception ( catchall )--maybeGetEnv :: String- -> IO (Maybe String)-maybeGetEnv s = fmap Just (getEnv s) `catchall` return Nothing -- err can only be isDoesNotExist
@@ -1,4 +1,3 @@-{-# LANGUAGE CPP #-} module Darcs.Util.External ( cloneTree , cloneFile
@@ -24,12 +24,11 @@ import System.Directory ( removeFile, getHomeDirectory, getAppUserDataDirectory, doesDirectoryExist, createDirectory, getDirectoryContents )-import System.IO.Error ( isDoesNotExistError )-import System.Posix.Files( getSymbolicLinkStatus, FileStatus, isDirectory+import System.IO.Error ( isDoesNotExistError, catchIOError )+import System.Posix.Files( getSymbolicLinkStatus, FileStatus, isDirectory ) #ifndef WIN32- , setFileMode, ownerModes+import System.Posix.Files( setFileMode, ownerModes ) #endif- ) import System.FilePath.Posix ( (</>) ) import Darcs.Util.Exception ( catchall )@@ -47,10 +46,9 @@ (\oldwd -> setCurrentDirectory oldwd `catchall` return ()) (const m) -getFileStatus :: FilePath- -> IO (Maybe FileStatus)+getFileStatus :: FilePath -> IO (Maybe FileStatus) getFileStatus f =- Just `fmap` getSymbolicLinkStatus f `catchall` return Nothing+ Just `fmap` getSymbolicLinkStatus f `catchIOError` (\_-> return Nothing) doesDirectoryReallyExist :: FilePath -> IO Bool doesDirectoryReallyExist f =
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- -- | -- Module : Darcs.Util.Global -- Copyright : 2005 Tomasz Zielonka
@@ -1,63 +1,269 @@--- Copyright (C) 2009-2011 Petr Rockai------ BSD3-{-# LANGUAGE DeriveDataTypeable #-}-module Darcs.Util.Hash( Hash(..)- , encodeBase16, decodeBase16, sha256, rawHash- , match ) where+-- Copyright (C) 2009-2011 Petr Rockai BSD3+-- Copyright (C) 2001, 2004 Ian Lynagh <igloo@earth.li> -import qualified Crypto.Hash.SHA256 as SHA256 ( hash )-import qualified Data.ByteString as BS+{-# LANGUAGE CPP, DeriveDataTypeable #-}+{-# OPTIONS_GHC -fno-warn-name-shadowing #-}++-- TODO switch to cryptonite++module Darcs.Util.Hash+ ( Hash(..)+ , encodeBase16, decodeBase16, sha256, sha256sum, rawHash+ , match+ -- SHA1 related (patch metadata hash)+ , sha1PS, SHA1, showAsHex, sha1Xor, sha1zero, sha1short+ ) where++import qualified Crypto.Hash.SHA256 as SHA256 ( hashlazy, hash )+import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Char8 as BS8+import qualified Data.ByteString.Char8 as BC+import qualified Data.ByteString.Internal as BI ( toForeignPtr ) import qualified Codec.Binary.Base16 as B16 import Data.Maybe( isJust, fromJust )-import Data.Char( toLower, toUpper )+import Data.Char( toLower, toUpper, intToDigit ) +import Data.Binary ( Binary(..) )+import Data.Bits (xor, (.&.), (.|.), complement, rotateL, shiftL, shiftR)+import Data.Word (Word8, Word32) import Data.Data( Data ) import Data.Typeable( Typeable )+import Foreign.ForeignPtr ( withForeignPtr )+import Foreign.Ptr (Ptr, castPtr, plusPtr)+import Foreign.Marshal.Array (advancePtr)+import Foreign.Storable (peek, poke)+import System.IO.Unsafe (unsafePerformIO) -data Hash = SHA256 !BS.ByteString- | SHA1 !BS.ByteString++data Hash = SHA256 !B.ByteString | NoHash deriving (Show, Eq, Ord, Read, Typeable, Data) -base16 :: BS.ByteString -> BS.ByteString-debase16 :: BS.ByteString -> Maybe BS.ByteString+base16 :: B.ByteString -> B.ByteString+debase16 :: B.ByteString -> Maybe B.ByteString -base16 = BS8.map toLower . B16.b16Enc-debase16 bs = case B16.b16Dec $ BS8.map toUpper bs of+base16 = BC.map toLower . B16.b16Enc+debase16 bs = case B16.b16Dec $ BC.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 :: Hash -> B.ByteString encodeBase16 (SHA256 bs) = base16 bs-encodeBase16 (SHA1 bs) = base16 bs-encodeBase16 NoHash = BS.empty+encodeBase16 NoHash = B.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)+decodeBase16 :: B.ByteString -> Hash+decodeBase16 bs | B.length bs == 64 && isJust (debase16 bs) = SHA256 (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.+-- | Compute a sha256 of a (lazy) ByteString. sha256 :: BL.ByteString -> Hash-sha256 bits = SHA256 $ SHA256.hash $ BS.concat $ BL.toChunks bits+sha256 bits = SHA256 $ SHA256.hashlazy bits -rawHash :: Hash -> BS.ByteString+-- | Same as previous but general purpose.+sha256sum :: B.ByteString -> String+sha256sum = BC.unpack . base16 . SHA256.hash++rawHash :: Hash -> B.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++data SHA1 = SHA1 !Word32 !Word32 !Word32 !Word32 !Word32+ deriving (Eq,Ord)+data XYZ = XYZ !Word32 !Word32 !Word32++instance Show SHA1 where+ show (SHA1 a b c d e) = concatMap showAsHex [a, b, c, d, e]++instance Binary SHA1 where+ put (SHA1 a b c d e) = put a >> put b >> put c >> put d >> put e+ get = do a <- get ; b <- get ; c <- get ; d <- get ; e <- get ; return (SHA1 a b c d e)++sha1Xor :: SHA1 -> SHA1 -> SHA1+sha1Xor (SHA1 a1 b1 c1 d1 e1) (SHA1 a2 b2 c2 d2 e2) =+ SHA1 (a1 `xor` a2) (b1 `xor` b2) (c1 `xor` c2) (d1 `xor` d2) (e1 `xor` e2)++sha1zero :: SHA1+sha1zero = SHA1 0 0 0 0 0++sha1short :: SHA1 -> Word32+sha1short (SHA1 a _ _ _ _) = a++-- | Do something with the internals of a PackedString. Beware of+-- altering the contents!+unsafeWithInternals :: B.ByteString -> (Ptr Word8 -> Int -> IO a) -> IO a+unsafeWithInternals ps f+ = case BI.toForeignPtr ps of+ (fp,s,l) -> withForeignPtr fp $ \p -> f (p `plusPtr` s) l++sha1PS:: B.ByteString -> SHA1+sha1PS s = abcde'+ where s1_2 = sha1Step12PadLength s+ abcde = sha1Step3Init+ abcde' = unsafePerformIO+ $ unsafeWithInternals s1_2 (\ptr len ->+ do let ptr' = castPtr ptr+#ifndef BIGENDIAN+ fiddleEndianness ptr' len+#endif+ sha1Step4Main abcde ptr' len)++fiddleEndianness :: Ptr Word32 -> Int -> IO ()+fiddleEndianness p 0 = p `seq` return ()+fiddleEndianness p n+ = do x <- peek p+ poke p $ shiftL x 24+ .|. shiftL (x .&. 0xff00) 8+ .|. (shiftR x 8 .&. 0xff00)+ .|. shiftR x 24+ fiddleEndianness (p `advancePtr` 1) (n - 4)++-- sha1Step12PadLength assumes the length is at most 2^61.+-- This seems reasonable as the Int used to represent it is normally 32bit,+-- but obviously could go wrong with large inputs on 64bit machines.+-- The B.ByteString library should probably move to Word64s if this is an+-- issue, though.++sha1Step12PadLength :: B.ByteString -> B.ByteString+sha1Step12PadLength s+ = let len = B.length s+ num_nuls = (55 - len) `mod` 64+ padding = 128:replicate num_nuls 0+ len_w8s = reverse $ sizeSplit 8 (fromIntegral len*8)+ in B.concat [s, B.pack padding, B.pack len_w8s]++sizeSplit :: Int -> Integer -> [Word8]+sizeSplit 0 _ = []+sizeSplit p n = fromIntegral d:sizeSplit (p-1) n'+ where (n', d) = divMod n 256++sha1Step3Init :: SHA1+sha1Step3Init = SHA1 0x67452301 0xefcdab89 0x98badcfe 0x10325476 0xc3d2e1f0++sha1Step4Main :: SHA1 -> Ptr Word32 -> Int -> IO SHA1+sha1Step4Main abcde _ 0 = return $! abcde+sha1Step4Main (SHA1 a0@a b0@b c0@c d0@d e0@e) s len+ = do+ (e, b) <- doit f1 0x5a827999 (x 0) a b c d e+ (d, a) <- doit f1 0x5a827999 (x 1) e a b c d+ (c, e) <- doit f1 0x5a827999 (x 2) d e a b c+ (b, d) <- doit f1 0x5a827999 (x 3) c d e a b+ (a, c) <- doit f1 0x5a827999 (x 4) b c d e a+ (e, b) <- doit f1 0x5a827999 (x 5) a b c d e+ (d, a) <- doit f1 0x5a827999 (x 6) e a b c d+ (c, e) <- doit f1 0x5a827999 (x 7) d e a b c+ (b, d) <- doit f1 0x5a827999 (x 8) c d e a b+ (a, c) <- doit f1 0x5a827999 (x 9) b c d e a+ (e, b) <- doit f1 0x5a827999 (x 10) a b c d e+ (d, a) <- doit f1 0x5a827999 (x 11) e a b c d+ (c, e) <- doit f1 0x5a827999 (x 12) d e a b c+ (b, d) <- doit f1 0x5a827999 (x 13) c d e a b+ (a, c) <- doit f1 0x5a827999 (x 14) b c d e a+ (e, b) <- doit f1 0x5a827999 (x 15) a b c d e+ (d, a) <- doit f1 0x5a827999 (m 16) e a b c d+ (c, e) <- doit f1 0x5a827999 (m 17) d e a b c+ (b, d) <- doit f1 0x5a827999 (m 18) c d e a b+ (a, c) <- doit f1 0x5a827999 (m 19) b c d e a+ (e, b) <- doit f2 0x6ed9eba1 (m 20) a b c d e+ (d, a) <- doit f2 0x6ed9eba1 (m 21) e a b c d+ (c, e) <- doit f2 0x6ed9eba1 (m 22) d e a b c+ (b, d) <- doit f2 0x6ed9eba1 (m 23) c d e a b+ (a, c) <- doit f2 0x6ed9eba1 (m 24) b c d e a+ (e, b) <- doit f2 0x6ed9eba1 (m 25) a b c d e+ (d, a) <- doit f2 0x6ed9eba1 (m 26) e a b c d+ (c, e) <- doit f2 0x6ed9eba1 (m 27) d e a b c+ (b, d) <- doit f2 0x6ed9eba1 (m 28) c d e a b+ (a, c) <- doit f2 0x6ed9eba1 (m 29) b c d e a+ (e, b) <- doit f2 0x6ed9eba1 (m 30) a b c d e+ (d, a) <- doit f2 0x6ed9eba1 (m 31) e a b c d+ (c, e) <- doit f2 0x6ed9eba1 (m 32) d e a b c+ (b, d) <- doit f2 0x6ed9eba1 (m 33) c d e a b+ (a, c) <- doit f2 0x6ed9eba1 (m 34) b c d e a+ (e, b) <- doit f2 0x6ed9eba1 (m 35) a b c d e+ (d, a) <- doit f2 0x6ed9eba1 (m 36) e a b c d+ (c, e) <- doit f2 0x6ed9eba1 (m 37) d e a b c+ (b, d) <- doit f2 0x6ed9eba1 (m 38) c d e a b+ (a, c) <- doit f2 0x6ed9eba1 (m 39) b c d e a+ (e, b) <- doit f3 0x8f1bbcdc (m 40) a b c d e+ (d, a) <- doit f3 0x8f1bbcdc (m 41) e a b c d+ (c, e) <- doit f3 0x8f1bbcdc (m 42) d e a b c+ (b, d) <- doit f3 0x8f1bbcdc (m 43) c d e a b+ (a, c) <- doit f3 0x8f1bbcdc (m 44) b c d e a+ (e, b) <- doit f3 0x8f1bbcdc (m 45) a b c d e+ (d, a) <- doit f3 0x8f1bbcdc (m 46) e a b c d+ (c, e) <- doit f3 0x8f1bbcdc (m 47) d e a b c+ (b, d) <- doit f3 0x8f1bbcdc (m 48) c d e a b+ (a, c) <- doit f3 0x8f1bbcdc (m 49) b c d e a+ (e, b) <- doit f3 0x8f1bbcdc (m 50) a b c d e+ (d, a) <- doit f3 0x8f1bbcdc (m 51) e a b c d+ (c, e) <- doit f3 0x8f1bbcdc (m 52) d e a b c+ (b, d) <- doit f3 0x8f1bbcdc (m 53) c d e a b+ (a, c) <- doit f3 0x8f1bbcdc (m 54) b c d e a+ (e, b) <- doit f3 0x8f1bbcdc (m 55) a b c d e+ (d, a) <- doit f3 0x8f1bbcdc (m 56) e a b c d+ (c, e) <- doit f3 0x8f1bbcdc (m 57) d e a b c+ (b, d) <- doit f3 0x8f1bbcdc (m 58) c d e a b+ (a, c) <- doit f3 0x8f1bbcdc (m 59) b c d e a+ (e, b) <- doit f2 0xca62c1d6 (m 60) a b c d e+ (d, a) <- doit f2 0xca62c1d6 (m 61) e a b c d+ (c, e) <- doit f2 0xca62c1d6 (m 62) d e a b c+ (b, d) <- doit f2 0xca62c1d6 (m 63) c d e a b+ (a, c) <- doit f2 0xca62c1d6 (m 64) b c d e a+ (e, b) <- doit f2 0xca62c1d6 (m 65) a b c d e+ (d, a) <- doit f2 0xca62c1d6 (m 66) e a b c d+ (c, e) <- doit f2 0xca62c1d6 (m 67) d e a b c+ (b, d) <- doit f2 0xca62c1d6 (m 68) c d e a b+ (a, c) <- doit f2 0xca62c1d6 (m 69) b c d e a+ (e, b) <- doit f2 0xca62c1d6 (m 70) a b c d e+ (d, a) <- doit f2 0xca62c1d6 (m 71) e a b c d+ (c, e) <- doit f2 0xca62c1d6 (m 72) d e a b c+ (b, d) <- doit f2 0xca62c1d6 (m 73) c d e a b+ (a, c) <- doit f2 0xca62c1d6 (m 74) b c d e a+ (e, b) <- doit f2 0xca62c1d6 (m 75) a b c d e+ (d, a) <- doit f2 0xca62c1d6 (m 76) e a b c d+ (c, e) <- doit f2 0xca62c1d6 (m 77) d e a b c+ (b, d) <- doit f2 0xca62c1d6 (m 78) c d e a b+ (a, c) <- doit f2 0xca62c1d6 (m 79) b c d e a+ let abcde' = SHA1 (a0 + a) (b0 + b) (c0 + c) (d0 + d) (e0 + e)+ sha1Step4Main abcde' (s `advancePtr` 16) (len - 64)+ where {-# INLINE f1 #-}+ f1 (XYZ x y z) = (x .&. y) .|. (complement x .&. z)+ {-# INLINE f2 #-}+ f2 (XYZ x y z) = x `xor` y `xor` z+ {-# INLINE f3 #-}+ f3 (XYZ x y z) = (x .&. y) .|. (x .&. z) .|. (y .&. z)+ {-# INLINE x #-}+ x n = peek (s `advancePtr` n)+ {-# INLINE m #-}+ m n = do let base = s `advancePtr` (n .&. 15)+ x0 <- peek base+ x1 <- peek (s `advancePtr` ((n - 14) .&. 15))+ x2 <- peek (s `advancePtr` ((n - 8) .&. 15))+ x3 <- peek (s `advancePtr` ((n - 3) .&. 15))+ let res = rotateL (x0 `xor` x1 `xor` x2 `xor` x3) 1+ poke base res+ return res+ {-# INLINE doit #-}+ doit f k i a b c d e = a `seq` c `seq`+ do i' <- i+ return (rotateL a 5 + f (XYZ b c d) + e + i' + k,+ rotateL b 30)++showAsHex :: Word32 -> String+showAsHex n = showIt 8 n ""+ where+ showIt :: Int -> Word32 -> String -> String+ showIt 0 _ r = r+ showIt i x r = case quotRem x 16 of+ (y, z) -> let c = intToDigit (fromIntegral z)+ in c `seq` showIt (i-1) y (c:r)
@@ -68,55 +68,64 @@ where import Prelude hiding ( lookup, readFile, writeFile, filter, (<$>) )-import Darcs.Util.ByteString ( readSegment )+import Darcs.Util.ByteString ( readSegment, decodeLocale )+import Darcs.Util.File ( getFileStatus )+import Darcs.Util.Hash( sha256, rawHash ) 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 Darcs.Util.Path+ ( AnchoredPath(..)+ , anchorPath+ , anchoredRoot+ , unsafeMakeName+ , appendPath+ , flatten+ ) 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 qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC 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 Data.Int( Int64, Int32 )+import Data.IORef( )+import Data.Maybe( fromJust, isJust, fromMaybe ) import Foreign.Storable import Foreign.ForeignPtr( ForeignPtr, withForeignPtr, castForeignPtr ) import Foreign.Ptr( Ptr, plusPtr ) -import Darcs.Util.Hash( sha256, rawHash )+import System.IO.MMap( mmapFileForeignPtr, mmapFileByteString, Mode(..) )+import System.Directory( doesFileExist, getCurrentDirectory, doesDirectoryExist )+#if mingw32_HOST_OS+import System.Directory( renameFile )+import System.FilePath( (<.>) )+#else+import System.Directory( removeFile )+#endif+ #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 )+import qualified System.Posix.Files as F ( getSymbolicLinkStatus, fileID ) #endif +import System.FilePath ( (</>) )+import qualified System.Posix.Files as F+ ( modificationTime, fileSize, isDirectory+ , FileStatus+ )+import System.Posix.Types ( FileID, EpochTime, FileOffset )+ -------------------------- -- Indexed trees --@@ -131,7 +140,7 @@ -- 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+ , iHashAndDescriptor :: !B.ByteString } deriving Show size_magic :: Int@@ -154,17 +163,19 @@ itemAllocSize :: AnchoredPath -> Int itemAllocSize apath =- align 4 $ size_hash + size_size + size_aux + size_fileid + size_dsclen + 2 + BS.length (flatten apath)+ align 4 $ size_hash + size_size + size_aux + size_fileid + size_dsclen + 2 + B.length (flatten apath) itemSize, itemNext :: Item -> Int-itemSize i = size_size + size_aux + size_fileid + size_dsclen + (BS.length $ iHashAndDescriptor i)+itemSize i = size_size + size_aux + size_fileid + size_dsclen + (B.length $ iHashAndDescriptor i) itemNext i = align 4 (itemSize i + 1) -iPath, iHash, iDescriptor :: Item -> BS.ByteString+iHash, iDescriptor :: Item -> B.ByteString iDescriptor = unsafeDrop size_hash . iHashAndDescriptor-iPath = unsafeDrop 1 . iDescriptor-iHash = BS.take size_hash . iHashAndDescriptor+iHash = B.take size_hash . iHashAndDescriptor +iPath :: Item -> FilePath+iPath = decodeLocale . unsafeDrop 1 . iDescriptor+ iSize, iAux :: Item -> Ptr Int64 iSize i = plusPtr (iBase i) off_size iAux i = plusPtr (iBase i) off_aux@@ -183,15 +194,29 @@ xlatePoke64 :: (Storable a, Num a, Bits a) => Ptr a -> a -> IO () xlatePoke64 ptr v = poke ptr (xlate64 v) +type FileStatus = Maybe F.FileStatus++modificationTime :: FileStatus -> EpochTime+modificationTime = maybe 0 F.modificationTime++fileSize :: FileStatus -> FileOffset+fileSize = maybe 0 F.fileSize++fileExists :: FileStatus -> Bool+fileExists = maybe False (const True)++isDirectory :: FileStatus -> Bool+isDirectory = maybe False F.isDirectory+ -- | 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'+ do let dsc = B.concat [ BC.singleton $ if typ == TreeType then 'D' else 'F' , flatten apath- , BS.singleton 0 ]+ , B.singleton 0 ] (dsc_fp, dsc_start, dsc_len) = toForeignPtr dsc withForeignPtr fp $ \p -> withForeignPtr dsc_fp $ \dsc_p ->@@ -221,7 +246,7 @@ -- when updating directory entries). updateItem :: Item -> Int64 -> Hash -> IO () updateItem item _ NoHash =- fail $ "Index.update NoHash: " ++ BSC.unpack (iPath item)+ fail $ "Index.update NoHash: " ++ iPath item updateItem item size hash = do xlatePoke64 (iSize item) size unsafePokeBS (iHash item) (rawHash hash)@@ -241,9 +266,7 @@ -- 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+ act_size <- fromIntegral . fileSize <$> getFileStatus indexpath let size = case req_size > 0 of True -> req_size False | act_size >= size_magic -> act_size - size_magic@@ -293,19 +316,19 @@ return res' readDir :: Index -> State -> Item -> IO Result-readDir index state item =- do following <- fromIntegral <$> xlatePeek64 (iAux item)- st <- getFileStatusBS (iPath item)+readDir index state item = do+ following <- fromIntegral <$> xlatePeek64 (iAux item)+ st <- getFileStatus (iPath item) let exists = fileExists st && isDirectory st fileid <- fromIntegral <$> (xlatePeek64 $ iFileID item)- fileid' <- fromMaybe fileid <$> (getFileID' $ BSC.unpack $ iPath item)+ fileid' <- fromMaybe fileid <$> (getFileID' $ 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)+ let name it dirlen = unsafeMakeName $ (B.drop (dirlen + 1) $ iDescriptor it) -- FIXME MAGIC+ namelength = (B.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 '.')+ , dirlength = if myname == unsafeMakeName (BC.singleton '.') then dirlength state else dirlength state + namelength } @@ -325,7 +348,7 @@ let we_changed = or [ changed x | (_, x) <- inferiors ] || nullleaf nullleaf = null inferiors && oldhash == nullsha- nullsha = SHA256 (BS.replicate 32 0)+ nullsha = SHA256 (B.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 }@@ -338,15 +361,15 @@ , resitem = item } readFile :: Index -> State -> Item -> IO Result-readFile index state item =- do st <- getFileStatusBS (iPath item)+readFile index state item = do+ st <- getFileStatus (iPath item) mtime <- fromIntegral <$> (xlatePeek64 $ iAux item) size <- xlatePeek64 $ iSize item fileid <- fromIntegral <$> (xlatePeek64 $ iFileID item)- fileid' <- fromMaybe fileid <$> (getFileID' $ BSC.unpack $ iPath item)+ fileid' <- fromMaybe fileid <$> (getFileID' $ iPath item) let mtime' = modificationTime st size' = fromIntegral $ fileSize st- readblob = readSegment (basedir index </> BSC.unpack (iPath item), Nothing)+ readblob = readSegment (basedir index </> (iPath item), Nothing) exists = fileExists st && not (isDirectory st) we_changed = mtime /= mtime' || size /= size' hash = iHash' item@@ -394,12 +417,12 @@ 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)+ let name it dirlen = unsafeMakeName $ (B.drop (dirlen + 1) $ iDescriptor it) -- FIXME MAGIC+ namelength = (B.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 '.')+ , dirlength = if myname == unsafeMakeName (BC.singleton '.') then dirlength state else dirlength state + namelength } subs off | off < following = do@@ -417,7 +440,7 @@ 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)+ let name it dirlen = unsafeMakeName $ (B.drop (dirlen + 1) $ iDescriptor it) myname = name item (dirlength state) return $ ResultF { nextF = start state + itemNext item , resitemF = item@@ -448,11 +471,11 @@ formatIndex :: ForeignPtr () -> Tree IO -> Tree IO -> IO () formatIndex mmap_ptr old reference = do _ <- create (SubTree reference) (AnchoredPath []) size_magic- unsafePokeBS magic (BSC.pack "HSI5")+ unsafePokeBS magic (BC.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'+ let flatpath = anchorPath "" path' case find old path' of Nothing -> return () -- TODO calling getFileStatus here is both slightly@@ -503,12 +526,13 @@ -- | 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+indexFormatValid path' = do+ v <- do magic <- mmapFileByteString path' (Just (0, size_magic))+ return $ case BC.unpack magic of+ "HSI5" -> True+ _ -> False+ `catch` \(_::SomeException) -> return False+ return v instance FilterTree IndexM IO where filter _ EmptyIndex = EmptyIndex@@ -530,13 +554,13 @@ closeHandle h return fhnumber #else- then (Just . fileID) <$> getSymbolicLinkStatus fp+ then (Just . F.fileID) <$> F.getSymbolicLinkStatus fp #endif else return Nothing -- Wow, unsafe.-unsafePokeBS :: BSC.ByteString -> BSC.ByteString -> IO ()+unsafePokeBS :: BC.ByteString -> BC.ByteString -> IO () unsafePokeBS to from = do let (fp_to, off_to, len_to) = toForeignPtr to (fp_from, off_from, len_from) = toForeignPtr from
@@ -26,7 +26,7 @@ -- Portability : portable module Darcs.Util.IsoDate- ( getIsoDateTime, readUTCDate+ ( getIsoDateTime, readUTCDate, readUTCDateOldFashioned , parseDate, getLocalTz , englishDateTime, englishInterval, englishLast , iso8601Interval, iso8601Duration@@ -34,6 +34,7 @@ , MCalendarTime(..), subtractFromMCal, addToMCal , toMCalendarTime, unsafeToCalendarTime , unsetTime, TimeInterval+ , showIsoDateTime ) where import Prelude ( (^) )@@ -45,7 +46,7 @@ import Data.Char ( toUpper, isDigit ) import Data.Maybe ( fromMaybe ) import Control.Monad ( liftM, liftM2 )-import qualified Data.ByteString.Char8 as B+import qualified Data.ByteString.Char8 as BC type TimeInterval = (Maybe CalendarTime, Maybe CalendarTime) @@ -77,25 +78,36 @@ Left e -> error $ "bad date: "++d++" - "++show e Right ct -> resetCalendar $ unsafeToCalendarTime ct +-- | Similar to 'readUTCDate', except we /ignore/ timezone info+-- in the input string. This is incorrect and ugly. The only reason+-- it still exists is so we can generate file names for old-fashioned+-- repositories in the same way that old darcs versions expected them.+-- You should not use this function except for the above stated purpose.+readUTCDateOldFashioned :: String -> CalendarTime+readUTCDateOldFashioned d = + case parseDate 0 d of+ Left e -> error $ "bad date: "++d++" - "++show e+ Right ct -> (unsafeToCalendarTime ct) { ctTZ = 0 }+ -- | Parse a date string, assuming a default timezone if -- the date string does not specify one. The date formats -- understood are those of 'showIsoDateTime' and 'dateTime' parseDate :: Int -> String -> Either ParseError MCalendarTime parseDate tz d =- if length d >= 14 && B.all isDigit bd+ if length d >= 14 && BC.all isDigit bd then Right $ toMCalendarTime $- CalendarTime (readI $ B.take 4 bd)- (toEnum $ (+ (-1)) $ readI $ B.take 2 $ B.drop 4 bd)- (readI $ B.take 2 $ B.drop 6 bd) -- Day- (readI $ B.take 2 $ B.drop 8 bd) -- Hour- (readI $ B.take 2 $ B.drop 10 bd) -- Minute- (readI $ B.take 2 $ B.drop 12 bd) -- Second+ CalendarTime (readI $ BC.take 4 bd)+ (toEnum $ (+ (-1)) $ readI $ BC.take 2 $ BC.drop 4 bd)+ (readI $ BC.take 2 $ BC.drop 6 bd) -- Day+ (readI $ BC.take 2 $ BC.drop 8 bd) -- Hour+ (readI $ BC.take 2 $ BC.drop 10 bd) -- Minute+ (readI $ BC.take 2 $ BC.drop 12 bd) -- Second 0 Sunday 0 -- Picosecond, weekday and day of year unknown "GMT" 0 False else let dt = do { x <- dateTime tz; eof; return x } in parse dt "" d- where bd = B.pack (take 14 d)- readI s = fst $ fromMaybe (error "parseDate: invalid date") (B.readInt s)+ where bd = BC.pack (take 14 d)+ readI s = fst $ fromMaybe (error "parseDate: invalid date") (BC.readInt s) -- | Display a 'CalendarTime' in the ISO 8601 format without any -- separators, e.g. 20080825142503
@@ -15,8 +15,6 @@ -- the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, -- Boston, MA 02110-1301, USA. -{-# LANGUAGE CPP #-}- module Darcs.Util.Lock ( withLock , withLockCanFail@@ -28,15 +26,14 @@ , withPermDir , withDelayedDir , withNamedTemp- , writeToFile- , appendToFile , writeBinFile- , writeLocaleFile+ , writeTextFile , writeDocBinFile , appendBinFile+ , appendTextFile , appendDocBinFile , readBinFile- , readLocaleFile+ , readTextFile , readDocBinFile , writeAtomicFilePS , gzWriteAtomicFilePS@@ -46,23 +43,31 @@ , removeFileMayNotExist , canonFilename , maybeRelink- , worldReadableTemp , tempdirLoc , environmentHelpTmpdir , environmentHelpKeepTmpdir , addToErrorLoc+ , withNewDirectory ) where +import Prelude ()+import Darcs.Prelude+ 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+ ( withFile, withBinaryFile, openBinaryTempFile+ , hClose, Handle, hPutStr, hSetEncoding+ , IOMode(WriteMode, AppendMode), hFlush, stdout+ ) import System.IO.Error ( isAlreadyExistsError+ , alreadyExistsErrorType , annotateIOError+ , mkIOError )+import System.IO.Temp ( withTempDirectory ) import Control.Exception ( IOException , bracket@@ -71,17 +76,28 @@ , try , SomeException )-import System.Directory ( removeFile, removeDirectory,- doesFileExist, doesDirectoryExist,- getDirectoryContents, createDirectory,- getTemporaryDirectory,- )+import System.Directory+ ( removeFile+ , removeDirectory+ , doesFileExist+ , doesDirectoryExist+ , getDirectoryContents+ , createDirectory+ , getTemporaryDirectory+ , renameDirectory+ ) import System.FilePath.Posix ( splitDirectories )+import qualified System.FilePath as NativeFilePath ( takeDirectory )+import System.Environment ( lookupEnv )+ import Control.Concurrent ( threadDelay ) import Control.Monad ( unless, when, liftM ) +import System.Posix.Files ( fileMode, getFileStatus, setFileMode )++import GHC.IO.Encoding ( getFileSystemEncoding )+ import Darcs.Util.URL ( isRelative )-import Darcs.Util.Environment ( maybeGetEnv ) import Darcs.Util.Exception ( firstJustIO , catchall@@ -91,12 +107,11 @@ import Darcs.Util.Path ( AbsolutePath, FilePathLike, toFilePath, getCurrentDirectory, setCurrentDirectory ) -import Darcs.Util.ByteString ( gzWriteFilePSs, decodeLocale, encodeLocale )+import Darcs.Util.ByteString ( gzWriteFilePSs ) 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.Printer ( Doc, hPutDoc, packedString, empty, renderPSs ) import Darcs.Util.AtExit ( atexit ) import Darcs.Util.Global ( darcsdir ) import Darcs.Util.Workaround ( renameFile )@@ -107,9 +122,8 @@ , atomicCreate , sloppyAtomicCreate )-import System.Posix.Files ( fileMode, getFileStatus, setFileMode )+import Darcs.Util.Progress ( debugMessage ) import Darcs.Util.Prompt ( askUser )-#include "impossible.h" withLock :: String -> IO a -> IO a withLock s job = bracket (getlock s 30) releaseLock (\_ -> job)@@ -173,7 +187,7 @@ -- -- 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 :: (FilePath -> IO a) -> IO a withTemp = bracket get_empty_file removeFileMayNotExist where get_empty_file = do (f,h) <- openBinaryTempFile "." "darcs" hClose h@@ -184,20 +198,20 @@ -- 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 :: ((Handle, FilePath) -> 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 :: (FilePath -> 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,+ firstJustIO [ liftM (Just . head . words) (readFile (darcsdir++"/prefs/tmpdir")) >>= chkdir,+ lookupEnv "DARCS_TMPDIR" >>= chkdir, getTemporaryDirectory >>= chkdir . Just, getCurrentDirectorySansDarcs, return $ Just "." -- always returns a Just@@ -227,7 +241,7 @@ -- 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+ -> FilePath -- path parameter -> (AbsolutePath -> IO a) -> IO a withDir _ "" _ = bug "withDir called with empty directory name" withDir kind absoluteOrRelativeName job = do@@ -253,7 +267,7 @@ `catch` (\e -> if isAlreadyExistsError e then createDir name (n+1) else throwIO e)- keepTempDir = isJust `fmap` maybeGetEnv "DARCS_KEEP_TMPDIR"+ keepTempDir = isJust `fmap` lookupEnv "DARCS_KEEP_TMPDIR" environmentHelpKeepTmpdir :: ([String], [String]) environmentHelpKeepTmpdir = (["DARCS_KEEP_TMPDIR"],[@@ -265,11 +279,11 @@ -- |'withPermDir' is like 'withTempDir', except that it doesn't -- delete the directory afterwards.-withPermDir :: String -> (AbsolutePath -> IO a) -> IO a+withPermDir :: FilePath -> (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+-- |'withTempDir' creates a temporary directory, runs the action and then+-- removes the 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@@ -283,10 +297,10 @@ -- 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 :: FilePath -> (AbsolutePath -> IO a) -> IO a withTempDir = withDir Temp -withDelayedDir :: String -> (AbsolutePath -> IO a) -> IO a+withDelayedDir :: FilePath -> (AbsolutePath -> IO a) -> IO a withDelayedDir = withDir Delayed rmRecursive :: FilePath -> IO ()@@ -302,50 +316,62 @@ do c <- getDirectoryContents d return $ filter (/=".") $ filter (/="..") c -worldReadableTemp :: String -> IO String+worldReadableTemp :: FilePath -> IO FilePath worldReadableTemp f = wrt 0- where wrt :: Int -> IO String+ where wrt :: Int -> IO FilePath 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+withNamedTemp :: FilePath -> (FilePath -> IO a) -> IO a+withNamedTemp n f = do+ debugMessage $ "withNamedTemp: " ++ show n+ bracket (worldReadableTemp n) removeFileMayNotExist f -readBinFile :: FilePathLike p => p -> IO String-readBinFile = fmap BC.unpack . B.readFile . toFilePath+readBinFile :: FilePathLike p => p -> IO B.ByteString+readBinFile = 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)+-- NOTE using 'seq' on the last element of the result causes the content to be+-- fully evaluated, so the file is read strictly; this is more efficient than+-- counting the number of characters; and in the (few) places where we use this+-- function we need the lines anyway.+readTextFile :: FilePathLike p => p -> IO [String]+readTextFile f = do+ result <- lines <$> readFile (toFilePath f)+ case result of+ [] -> return result+ xs -> last xs `seq` return result 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+appendBinFile :: FilePathLike p => p -> B.ByteString -> IO ()+appendBinFile f s = appendToFile Binary f $ \h -> B.hPut h s +appendTextFile :: FilePathLike p => p -> String -> IO ()+appendTextFile f s = appendToFile Text f $ \h -> hPutStr h s+ appendDocBinFile :: FilePathLike p => p -> Doc -> IO ()-appendDocBinFile f d = appendToFile f $ \h -> hPutDoc Standard h d+appendDocBinFile f d = appendToFile Binary f $ \h -> hPutDoc h d -writeBinFile :: FilePathLike p => p -> String -> IO ()-writeBinFile f s = writeToFile f $ \h -> hPutStr h s+data FileType = Text | Binary --- | 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)+writeBinFile :: FilePathLike p => p -> B.ByteString -> IO ()+writeBinFile f s = writeToFile Binary f $ \h -> B.hPut h s +writeTextFile :: FilePathLike p => p -> String -> IO ()+writeTextFile f s = writeToFile Text f $ \h -> do+ getFileSystemEncoding >>= hSetEncoding h+ hPutStr h s+ writeDocBinFile :: FilePathLike p => p -> Doc -> IO ()-writeDocBinFile f d = writeToFile f $ \h -> hPutDoc Standard h d+writeDocBinFile f d = writeToFile Binary f $ \h -> hPutDoc h d writeAtomicFilePS :: FilePathLike p => p -> B.ByteString -> IO ()-writeAtomicFilePS f ps = writeToFile f $ \h -> B.hPut h ps+writeAtomicFilePS f ps = writeToFile Binary f $ \h -> B.hPut h ps gzWriteAtomicFilePS :: FilePathLike p => p -> B.ByteString -> IO () gzWriteAtomicFilePS f ps = gzWriteAtomicFilePSs f [ps]@@ -361,24 +387,43 @@ renameFile newf (toFilePath f) gzWriteDocFile :: FilePathLike p => p -> Doc -> IO ()-gzWriteDocFile f d = gzWriteAtomicFilePSs f $ renderPSs Standard d+gzWriteDocFile f d = gzWriteAtomicFilePSs f $ renderPSs d -writeToFile :: FilePathLike p => p -> (Handle -> IO ()) -> IO ()-writeToFile f job =+writeToFile :: FilePathLike p => FileType -> p -> (Handle -> IO ()) -> IO ()+writeToFile t f job = withSignalsBlocked $ withNamedTemp (toFilePath f) $ \newf -> do- withBinaryFile newf WriteMode job+ (case t of+ Text -> withFile+ Binary -> 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+appendToFile :: FilePathLike p => FileType -> p -> (Handle -> IO ()) -> IO ()+appendToFile t f job = withSignalsBlocked $+ (case t of+ Binary -> withBinaryFile+ Text -> withFile) (toFilePath f) AppendMode job addToErrorLoc :: IOException -> String -> IOException addToErrorLoc ioe s = annotateIOError ioe s Nothing Nothing++-- | Do an action in a newly created directory of the given name. The directory+-- is first created with a different temporary name (but under the same parent+-- directory), and becomes renamed to the final name when the action returns+-- without an exception. This operation raises an alreadyExists exception if a+-- file system object of the given name already exists.+withNewDirectory :: FilePath -> IO () -> IO ()+withNewDirectory name action = do+ exists1 <- doesFileExist name -- TODO when depending on directory >= 1.2.7+ exists2 <- doesDirectoryExist name -- just use doesPathExist+ when (exists1 || exists2) $+ throwIO (mkIOError alreadyExistsErrorType name Nothing Nothing)+ withTempDirectory (NativeFilePath.takeDirectory name) "darcs_new" $ \path -> do+ withCurrentDirectory path action+ renameDirectory path name
@@ -28,8 +28,6 @@ , fn2fp , fn2ps , ps2fn- , niceps2fn- , fn2niceps , breakOnDir , normPath , ownName@@ -37,6 +35,8 @@ , movedirfilename , encodeWhite , decodeWhite+ , encodeWhiteName+ , decodeWhiteName , isParentOrEqOf -- * AbsolutePath , AbsolutePath@@ -76,7 +76,9 @@ -- * 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(..)+ , Name+ , unsafeMakeName+ , eqAnycase , AnchoredPath(..) , anchoredRoot , appendPath@@ -84,7 +86,7 @@ , isPrefix , parent, parents, catPaths, flatten, makeName, appendToName -- * Unsafe AnchoredPath functions.- , floatBS, floatPath, replacePrefixPath ) where+ , floatPath, replacePrefixPath ) where import Prelude () import Darcs.Prelude@@ -96,7 +98,7 @@ , intersect , inits )-import Data.Char ( isSpace, chr, ord )+import Data.Char ( isSpace, chr, ord, toLower ) import Control.Exception ( tryJust, bracket_ ) import Control.Monad ( when ) import System.IO.Error ( isDoesNotExistError )@@ -109,17 +111,14 @@ import System.FilePath( (</>), splitDirectories, normalise, dropTrailingPathSeparator ) import System.Posix.Files ( isDirectory, getSymbolicLinkStatus ) -import Darcs.Util.ByteString ( packStringToUTF8, unpackPSFromUTF8 )+import Darcs.Util.ByteString ( encodeLocale, decodeLocale ) import qualified Data.ByteString.Char8 as BC-import qualified Data.ByteString as B (ByteString)+import qualified Data.ByteString as B 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 )@@ -140,21 +139,13 @@ fn2fp :: FileName -> FilePath fn2fp (FN fp) = fp -{-# INLINE niceps2fn #-}-niceps2fn :: B.ByteString -> FileName-niceps2fn = FN . decodeWhite . BC.unpack--{-# INLINE fn2niceps #-}-fn2niceps :: FileName -> B.ByteString-fn2niceps (FN fp) = BC.pack $ encodeWhite fp- {-# INLINE fn2ps #-} fn2ps :: FileName -> B.ByteString-fn2ps (FN fp) = packStringToUTF8 $ encodeWhite fp+fn2ps (FN fp) = encodeLocale $ encodeWhite fp {-# INLINE ps2fn #-} ps2fn :: B.ByteString -> FileName-ps2fn ps = FN $ decodeWhite $ unpackPSFromUTF8 ps+ps2fn ps = FN $ decodeWhite $ decodeLocale ps {-# INLINE sp2fn #-} sp2fn :: SubPath -> FileName@@ -190,6 +181,12 @@ _ -> error "malformed filename" go (c:cs) acc modified = go cs (c:acc) modified +encodeWhiteName :: Name -> B.ByteString+encodeWhiteName = encodeLocale . encodeWhite . decodeLocale . unName++decodeWhiteName :: B.ByteString -> Name+decodeWhiteName = Name . encodeLocale . decodeWhite . decodeLocale+ ownName :: FileName -> FileName ownName (FN f) = case breakLast '/' f of Nothing -> FN f Just (_,f') -> FN f'@@ -568,7 +565,7 @@ -- AnchoredPath utilities -- -newtype Name = Name BC.ByteString deriving (Eq, Show, Ord)+newtype Name = Name { unName :: B.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@@ -586,7 +583,7 @@ appendPath :: AnchoredPath -> Name -> AnchoredPath appendPath (AnchoredPath p) n = case n of- (Name s) | s == BC.empty -> AnchoredPath p+ (Name s) | B.null s -> AnchoredPath p | s == BC.pack "." -> AnchoredPath p | otherwise -> AnchoredPath $ p ++ [n] @@ -608,14 +605,9 @@ -- 'FilePath'. Moreover, you can use @anchorPath \"\"@ to get a relative -- 'FilePath'. anchorPath :: FilePath -> AnchoredPath -> FilePath-anchorPath dir p = dir </> BC.unpack (flatten p)+anchorPath dir p = dir </> decodeLocale (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 '/')@@ -624,7 +616,7 @@ 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+ | otherwise = Name $ encodeLocale n -- | Take a relative FilePath and turn it into an AnchoredPath. The operation -- is (relatively) unsafe. Basically, by using floatPath, you are testifying@@ -637,7 +629,7 @@ floatPath :: FilePath -> AnchoredPath floatPath = make . splitDirectories . normalise . dropTrailingPathSeparator where make ["."] = AnchoredPath []- make x = AnchoredPath $ map (Name . BC.pack) x+ make x = AnchoredPath $ map (Name . encodeLocale) x anchoredRoot :: AnchoredPath@@ -653,11 +645,17 @@ | otherwise = AnchoredPath [] replacePrefixPath _ _ _ = AnchoredPath [] --- | Append a ByteString to the last Name of an AnchoredPath.+-- | Append a String 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+ where suffix = encodeLocale s finalname | suffix `elem` (BC.tails lastname) = lastname | otherwise = BC.append lastname suffix lastname = case last p of Name name -> name++unsafeMakeName :: B.ByteString -> Name+unsafeMakeName = Name++eqAnycase :: Name -> Name -> Bool+eqAnycase (Name a) (Name b) = BC.map toLower a == BC.map toLower b
@@ -52,9 +52,10 @@ , insertBeforeLastline , prefixLines , invisiblePS, userchunkPS- -- * Rendering- , RenderMode(..)- , renderString, renderStringWith, renderPS, renderPSWith+ -- * Rendering to 'String'+ , renderString, renderStringWith+ -- * Rendering to 'ByteString'+ , renderPS, renderPSWith , renderPSs, renderPSsWith -- * Printers , Printers@@ -70,14 +71,14 @@ , blueText, redText, greenText, magentaText, cyanText , colorText , lineColor- -- * IO+ -- * IO, uses 'Data.ByteString.hPut' for output , hPutDoc, hPutDocLn, putDoc, putDocLn , hPutDocWith, hPutDocLnWith, putDocWith, putDocLnWith , hPutDocCompr , debugDocLn , ePutDocLn , errorDoc- -- * Unsafe constructors+ -- * TODO: It is unclear what is unsafe about these constructors , unsafeText, unsafeBoth, unsafeBothText, unsafeChar , unsafePackedString ) where@@ -85,17 +86,12 @@ 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, 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)+import System.IO ( Handle, stdout, stderr )+import qualified Data.ByteString as B ( ByteString, hPut, concat )+import qualified Data.ByteString.Char8 as BC ( singleton ) -import Darcs.Util.ByteString ( linesPS, encodeLocale, gzWriteHandle )+import Darcs.Util.ByteString ( linesPS, decodeLocale, encodeLocale, gzWriteHandle ) import Darcs.Util.Global ( debugMessage ) -- | A 'Printable' is either a String, a packed string, or a chunk of@@ -146,74 +142,71 @@ -- | Fail with a stack trace and the given 'Doc' as error message. errorDoc :: Doc -> a-errorDoc x = unsafePerformIO $ do- stack <- currentCallStack- throwIO $ ErrorCall $ renderString Encode $ x $$ vcat (map text stack)+errorDoc x = error $ renderString x --- | 'putDocWith' puts a doc on stdout using the given printer.+-- | 'putDocWith' puts a 'Doc' on stdout using the given printer. putDocWith :: Printers -> Doc -> IO ()-putDocWith prs = hPutDocWith prs Encode stdout+putDocWith prs = hPutDocWith prs stdout --- | 'putDocLnWith' puts a doc, followed by a newline on stdout using+-- | 'putDocLnWith' puts a 'Doc', followed by a newline on stdout using -- the given printer. putDocLnWith :: Printers -> Doc -> IO ()-putDocLnWith prs = hPutDocLnWith prs Encode stdout+putDocLnWith prs = hPutDocLnWith prs stdout --- | 'putDoc' puts a doc on stdout using the simple printer 'simplePrinters'.+-- | 'putDoc' puts a 'Doc' on stdout using the simple printer 'simplePrinters'. putDoc :: Doc -> IO ()-putDoc = hPutDoc Encode stdout+putDoc = hPutDoc stdout --- | 'putDocLn' puts a doc, followed by a newline on stdout using+-- | 'putDocLn' puts a 'Doc', followed by a newline on stdout using -- 'simplePrinters' putDocLn :: Doc -> IO ()-putDocLn = hPutDocLn Encode stdout+putDocLn = hPutDocLn stdout --- | 'eputDocLn' puts a doc, followed by a newline to stderr using+-- | '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+ePutDocLn = hPutDocLn 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)+-- | 'hputDocWith' puts a 'Doc' on the given handle using the given printer.+hPutDocWith :: Printers -> Handle -> Doc -> IO ()+hPutDocWith prs h d = hPrintPrintables h (renderWith (prs h) d) --- | 'hputDocLnWith' puts a doc, followed by a newline on the given+-- | 'hputDocLnWith' puts a 'Doc', followed by a newline on the given -- handle using the given printer.-hPutDocLnWith :: Printers -> RenderMode -> Handle -> Doc -> IO ()-hPutDocLnWith prs target h d = hPutDocWith prs target h (d <?> newline)+hPutDocLnWith :: Printers -> Handle -> Doc -> IO ()+hPutDocLnWith prs h d = hPutDocWith prs h (d <?> newline) --- |'hputDoc' puts a doc on the given handle using 'simplePrinters'-hPutDoc :: RenderMode -> Handle -> Doc -> IO ()+-- |'hputDoc' puts a 'Doc' on the given handle using 'simplePrinters'+hPutDoc :: 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 :: Handle -> Doc -> IO () hPutDocLn = hPutDocLnWith simplePrinters -- | like 'hPutDoc' but with compress data before writing-hPutDocCompr :: RenderMode -> Handle -> Doc -> IO ()-hPutDocCompr target h = gzWriteHandle h . renderPSs target+hPutDocCompr :: Handle -> Doc -> IO ()+hPutDocCompr h = gzWriteHandle h . renderPSs -- | Write a 'Doc' to stderr if debugging is turned on. debugDocLn :: Doc -> IO ()-debugDocLn = debugMessage . renderString Standard+debugDocLn = debugMessage . renderString -- | @'hPrintPrintables' h@ prints a list of 'Printable's to the handle @h@-hPrintPrintables :: RenderMode -> Handle -> [Printable] -> IO ()-hPrintPrintables target h = mapM_ (hPrintPrintable target h)+-- It uses binary output of 'ByteString's. If these not available,+-- converts according to locale.+hPrintPrintables :: Handle -> [Printable] -> IO ()+hPrintPrintables h = mapM_ (hPrintPrintable 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)-hPrintPrintable Standard h (PS ps) = B.hPut h ps-hPrintPrintable Encode h (PS ps) = B.hPut h ps-hPrintPrintable Standard h (Both _ ps) = B.hPut h ps-hPrintPrintable Encode h (Both _ ps) = B.hPut h ps+hPrintPrintable :: Handle -> Printable -> IO ()+hPrintPrintable h (S ps) = B.hPut h (encodeLocale ps)+hPrintPrintable h (PS ps) = B.hPut h ps+hPrintPrintable h (Both _ ps) = B.hPut h ps -- | A 'Doc' is a bit of enriched text. 'Doc's are concatenated using -- '<>' from class 'Monoid', which is right-associative.@@ -224,22 +217,7 @@ instance IsString Doc where fromString = text --- TODO this is a rather ad-hoc hack that further complicates--- some already confusing code. We should find a more general--- solution. See the discussion on issue1639.--- | Used when rendering a 'Doc' to indicate if the result--- should be encoded to the current locale or left alone.--- In practice this only affects output when a relevant--- DARCS_DONT_ESCAPE_XXX option is set (see Darcs.Util.Printer.Color)--- If in doubt, choose 'Standard'.-data RenderMode =- Encode -- ^Encode Strings with the current locale.- -- At present ByteStrings are assumed to be in- -- UTF8 and are left alone, so will be mis-encoded- -- in non-UTF8 locales.- | Standard -- ^Don't encode.---- | The State associated with a doc. Contains a set of printers for each+-- | The State associated with a 'Doc'. Contains a set of printers for each -- hanlde, and the current prefix of the document. data St = St { printers :: !Printers', currentPrefix :: !([Printable] -> [Printable]) }@@ -259,54 +237,50 @@ data Color = Blue | Red | Green | Cyan | Magenta -- | 'Document' is a wrapper around '[Printable] -> [Printable]' which allows--- for empty Documents. The simplest 'Documents' are built from 'String's--- using 'text'.+-- to handle the special case of an empty 'Document' in a non-uniform manner.+-- The simplest 'Documents' are built from 'String's using 'text'. data Document = Document ([Printable] -> [Printable]) | Empty -- | renders a 'Doc' into a 'String' with control codes for the--- special features of the doc.-renderString :: RenderMode -> Doc -> String+-- special features of the 'Doc'.+renderString :: Doc -> String renderString = renderStringWith simplePrinters' -- | renders a 'Doc' into a 'String' using a given set of printers.-renderStringWith :: Printers' -> RenderMode -> Doc -> String-renderStringWith prs target d = concatMap (toString target) $ renderWith prs d- where toString Standard (S s) = s- toString Encode (S s) = BC.unpack . encodeLocale $ s- toString Standard (PS ps) = BC.unpack ps- toString Encode (PS ps) = BC.unpack ps- toString Standard (Both s _) = s- toString Encode (Both s _) = BC.unpack . encodeLocale $ s+-- If content is only available as 'ByteString', decode according to+-- the current locale.+renderStringWith :: Printers' -> Doc -> String+renderStringWith prs d = concatMap (toString) $ renderWith prs d+ where toString (S s) = s+ toString (PS ps) = decodeLocale ps+ toString (Both s _) = s -- | renders a 'Doc' into 'B.ByteString' with control codes for the -- special features of the Doc. See also 'readerString'.-renderPS :: RenderMode -> Doc -> B.ByteString+renderPS :: Doc -> B.ByteString renderPS = renderPSWith simplePrinters' -- | renders a 'Doc' into a list of 'PackedStrings', one for each line.-renderPSs :: RenderMode -> Doc -> [B.ByteString]+renderPSs :: Doc -> [B.ByteString] renderPSs = renderPSsWith simplePrinters' --- | renders a doc into a 'B.ByteString' using a given set of printers.-renderPSWith :: Printers' -> RenderMode -> Doc -> B.ByteString-renderPSWith prs target d = B.concat $ renderPSsWith prs target d+-- | renders a 'Doc' into a 'B.ByteString' using a given set of printers.+renderPSWith :: Printers' -> Doc -> B.ByteString+renderPSWith prs d = B.concat $ renderPSsWith prs d -- | renders a 'Doc' into a list of 'PackedStrings', one for each--- chunk of text that was added to the doc, using the given set of+-- chunk of text that was added to the 'Doc', using the given set of -- printers.-renderPSsWith :: Printers' -> RenderMode -> Doc -> [B.ByteString]-renderPSsWith prs target d = map (toPS target) $ renderWith prs d- where toPS Standard (S s) = BC.pack s- toPS Encode (S s) = encodeLocale s- toPS Standard (PS ps) = ps- toPS Encode (PS ps) = ps- toPS Standard (Both _ ps) = ps- toPS Encode (Both _ ps) = ps+renderPSsWith :: Printers' -> Doc -> [B.ByteString]+renderPSsWith prs d = map toPS $ renderWith prs d+ where toPS (S s) = encodeLocale s+ toPS (PS ps) = ps+ toPS (Both _ ps) = ps -- | renders a 'Doc' into a list of 'Printables' using a set of -- printers. Each item of the list corresponds to a string that was--- added to the doc.+-- added to the 'Doc'. renderWith :: Printers' -> Doc -> [Printable] renderWith ps (Doc d) = case d (initState ps) of Empty -> []@@ -327,21 +301,16 @@ -- of the Doc abstraction prefixLines :: Doc -> Doc -> Doc prefixLines prefixer prefixee =- vcat $ map (prefixer <+>) $ map packedString $ linesPS $- -- this will just get round-tripped back into a Doc,- renderPS Standard prefixee+ vcat $ map (prefixer <+>) $ map packedString $ linesPS $ renderPS prefixee -- TODO try to find another way to do this, it's rather a violation -- of the Doc abstraction insertBeforeLastline :: Doc -> Doc -> Doc insertBeforeLastline a b =- -- as this will just get round-tripped back into a Doc,- -- we use 'Standard' as the Target type so the encoding- -- is left alone- case reverse $ map packedString $ linesPS $ renderPS Standard a of- (ll:ls) -> vcat (reverse ls) $$ b $$ ll- [] -> error "empty Doc given as first argument of Printer.insert_before_last_line"-+ case reverse $ map packedString $ linesPS $ renderPS a of+ (ll:ls) -> vcat (reverse ls) $$ b $$ ll+ [] ->+ error "empty Doc given as first argument of Printer.insert_before_last_line" lineColor :: Color -> Doc -> Doc lineColor c d = Doc $ \st -> case lineColorT (printers st) c d of@@ -350,7 +319,7 @@ hiddenPrefix :: String -> Doc -> Doc hiddenPrefix s (Doc d) = Doc $ \st -> let pr = printers st- p = S (renderStringWith pr Standard $ hiddenText s)+ p = S (renderStringWith pr $ hiddenText s) st' = st { currentPrefix = currentPrefix st . (p:) } in case d st' of Document d'' -> Document $ (p:) . d''@@ -364,7 +333,7 @@ -- | 'unsafeBothText' builds a 'Doc' from a 'String'. The string is stored in the -- Doc as both a String and a 'B.ByteString'. unsafeBothText :: String -> Doc-unsafeBothText s = Doc $ simplePrinter (Both s (BC.pack s))+unsafeBothText s = Doc $ simplePrinter (Both s (encodeLocale s)) -- | 'packedString' builds a 'Doc' from a 'B.ByteString' using 'printable' packedString :: B.ByteString -> Doc@@ -478,6 +447,7 @@ infixr 6 `append` infixr 6 <+>+infixr 5 `vplus` infixr 5 $$ -- | The empty 'Doc'@@ -487,6 +457,9 @@ doc :: ([Printable] -> [Printable]) -> Doc doc f = Doc $ const $ Document f +instance Semigroup Doc where+ (<>) = append+ -- | 'mappend' ('<>') is concatenation, 'mempty' is the 'empty' 'Doc' instance Monoid Doc where mempty = empty@@ -511,7 +484,7 @@ Empty -> s Document bf -> bf s) --- | @a '<+>' b@ is @a@ followed by a space, then @b@+-- | @a '<+>' b@ is @a@ followed by @b@ with a space in between if both are non-empty (<+>) :: Doc -> Doc -> Doc Doc a <+> Doc b = Doc $ \st -> case a st of@@ -533,15 +506,25 @@ where pf = currentPrefix st sf = lineColorS $ printers st +-- | @vplus a b@ is @a@ above @b@ with an empty line in between if both are non-empty+vplus :: Doc -> Doc -> Doc+Doc a `vplus` Doc b =+ Doc $ \st -> case a st of+ Empty -> b st+ Document af ->+ Document (\s -> af $ case b st of+ Empty -> s+ Document bf -> sf (newlineP:newlineP:pf (bf s)))+ where pf = currentPrefix st+ sf = lineColorS $ printers st+ -- | Pile 'Doc's vertically vcat :: [Doc] -> Doc-vcat [] = empty-vcat ds = foldr1 ($$) ds+vcat = foldr ($$) empty -- | Pile 'Doc's vertically, with a blank line in between vsep :: [Doc] -> Doc-vsep [] = empty-vsep ds = foldr1 ($$) $ intersperse (text "") ds+vsep = foldr vplus empty -- | Concatenate 'Doc's horizontally hcat :: [Doc] -> Doc
@@ -8,18 +8,16 @@ import Darcs.Prelude import Darcs.Util.Printer- ( Printer, Printers, Printers'(..), Printable(..), Color(..), RenderMode(..)+ ( Printer, Printers, Printers'(..), Printable(..), Color(..) , invisiblePrinter, (<>), (<?>), Doc(Doc,unDoc), unsafeBothText, simplePrinter, hcat , unsafeText, unsafePackedString , renderStringWith, prefix ) -import Control.Monad ( liftM )-import Control.Exception ( catch, IOException ) import Debug.Trace ( trace ) import Data.Char ( isAscii, isPrint, isSpace, isControl, ord, chr ) import Data.Bits ( bit, xor )-import System.Environment ( getEnv )+import System.Environment ( lookupEnv ) import qualified Data.ByteString.Char8 as BC (unpack, any, last, spanEnd) import qualified Data.ByteString as B (null, init) import System.IO.Unsafe ( unsafePerformIO )@@ -35,20 +33,19 @@ cr = unsafeBothText "\r" errorDoc :: Doc -> a-errorDoc = error . showDoc Encode+errorDoc = error . showDoc traceDoc :: Doc -> a -> a-traceDoc d = trace (showDoc Encode d)+traceDoc d = trace (showDoc d) assertDoc :: Maybe Doc -> a -> a assertDoc Nothing x = x assertDoc (Just e) _ = errorDoc e -showDoc :: RenderMode -> Doc -> String+showDoc :: Doc -> String showDoc = renderStringWith (fancyPrinters stderr) --- policy--- | the 'Policy' type is a record containing the variables which control+-- | The 'Policy' type is a record containing the variables which control -- how 'Doc's will be rendered on some output. data Policy = Policy { poColor :: Bool -- ^ overall use of color , poEscape :: Bool -- ^ overall use of escaping@@ -74,8 +71,8 @@ envDontEscapeAnything <- getEnvBool "DARCS_DONT_ESCAPE_ANYTHING" envDontEscapeIsprint <- getEnvBool "DARCS_DONT_ESCAPE_ISPRINT"- envUseIsprint <- getEnvBool "DARCS_USE_ISPRINT" -- for backwards-compatibility- envDontEscape8bit <- getEnvBool "DARCS_DONT_ESCAPE_8BIT"+ envUseIsprint <- getEnvBool "DARCS_USE_ISPRINT"+ envEscape8bit <- getEnvBool "DARCS_ESCAPE_8BIT" envDontEscapeExtra <- getEnvString "DARCS_DONT_ESCAPE_EXTRA" envEscapeExtra <- getEnvString "DARCS_ESCAPE_EXTRA"@@ -95,7 +92,7 @@ poEscape = not envDontEscapeAnything, poLineColor= doColor && envDoColorLines, poIsprint = envDontEscapeIsprint || envUseIsprint,- po8bit = envDontEscape8bit,+ po8bit = not envEscape8bit, poNoEscX = envDontEscapeExtra, poEscX = envEscapeExtra, poTrailing = not envDontEscapeTrailingSpace,@@ -105,9 +102,8 @@ poSpace = False } where- getEnvBool s = (/= "0") `liftM` safeGetEnv s- safeGetEnv s = getEnv s `catch` \(_ :: IOException) -> return "0"- getEnvString s = getEnv s `catch` \(_ :: IOException) -> return ""+ getEnvBool s = maybe False (/= "0") <$> lookupEnv s+ getEnvString s = maybe "" id <$> lookupEnv s {-@@ -336,47 +332,47 @@ environmentHelpEscape :: ([String], [String]) environmentHelpEscape = (["DARCS_DONT_ESCAPE_ANYTHING",- "DARCS_DONT_ESCAPE_ISPRINT",- "DARCS_DONT_ESCAPE_8BIT", "DARCS_DONT_ESCAPE_EXTRA",- "DARCS_ESCAPE_EXTRA"],[+ "DARCS_ESCAPE_EXTRA",+ "DARCS_DONT_ESCAPE_ISPRINT",+ "DARCS_ESCAPE_8BIT"],[ "Darcs needs to escape certain characters when printing patch contents to",- "a terminal. Characters like backspace can otherwise hide patch content",- "from the user, and other character sequences can even in some cases",- "redirect commands to the shell if the terminal allows it.",+ "a terminal, depending on the encoding specified in your locale setting.", "",- "By default darcs will only allow printable 7-bit ASCII",- "characters (including space), and the two control characters tab and",- "newline. All other octets are printed in quoted form (as `^<control letter>`",- "or `\\<hex code>`).",+ "By default, darcs assumes that your locale encoding is ASCII compatible.",+ "This includes UTF-8 and some 8-bit encodings like ISO/IEC-8859 (including",+ "its variants). Since ASCII contains control characters like backspace",+ "(which could hide patch content from the user when printed literally to",+ "the terminal), and even ones that may introduce security risks such as",+ "redirecting commands to the shell, darcs needs to escape such characters.",+ "They are printed as `^<control letter>` or `\\<hex code>`. Darcs also uses",+ "special markup for line endings that are preceeded by white space, since",+ "the white space would otherwise not be recognizable.", "",- "Darcs has some limited support for locales. If the system's locale is a ",- "single-byte character encoding, like the Latin encodings, you can set the",- "environment variable DARCS_DONT_ESCAPE_ISPRINT to 1 and darcs will display",- "all the printables in the current system locale instead of just the ASCII",- "ones. NOTE: This curently does not work on some architectures if darcs",- "is compiled with GHC 6.4 or later. Some non-ASCII control characters might",- "be printed and can possibly spoof the terminal.",+ "If you use an encoding that is not ASCII compatible, things are somewhat",+ "less smooth. Such encodings include UTF-16 and UTF-32, as well as many of",+ "the encodings that became obsolete with unicode. In this case you have two",+ "options: you can set DARCS_DONT_ESCAPE_ANYTHING to 1. Then everything that",+ "doesn't flip code sets should work, and so will all the bells and whistles",+ "in your terminal. This environment variable can also be handy if you pipe",+ "the output to a pager or external filter that knows better than darcs how to",+ "handle your encoding. Note that all escaping, including the special escaping",+ "of any line ending spaces, will be turned off by this setting.", "",- "For multi-byte character encodings things are less smooth. UTF-8 will",- "work if you set DARCS_DONT_ESCAPE_8BIT to 1, but non-printables outside",- "the 7-bit ASCII range are no longer escaped. E.g., the extra control",- "characters from Latin-1 might leave your terminal at the mercy of the",- "patch contents. Space characters outside the 7-bit ASCII range are no",- "longer recognized and will not be properly escaped at line endings.",+ "Another possibility is to explicitly tell darcs to not escape or escape",+ "certain bytes, using DARCS_DONT_ESCAPE_EXTRA and DARCS_ESCAPE_EXTRA. Their",+ "values should be strings consisting of the verbatim bytes in question. The",+ "do-escapes take precedence over the dont-escapes. Space characters are still",+ "escaped at line endings though. The special environment variable",+ "DARCS_DONT_ESCAPE_TRAILING_CR turns off escaping of carriage return last on",+ "the line (DOS style).", "",- "As a last resort you can set DARCS_DONT_ESCAPE_ANYTHING to 1. Then",- "everything that doesn't flip code sets should work, and so will all the",- "bells and whistles in your terminal. This environment variable can also",- "be handy if you pipe the output to a pager or external filter that knows",- "better than darcs how to handle your encoding. Note that all escaping,",- "including the special escaping of any line ending spaces, will be turned",- "off by this setting.",+ "For historical reasons, darcs also supports DARCS_DONT_ESCAPE_ISPRINT and",+ "DARCS_USE_ISPRINT (which are synonyms). These make sense only for 8-bit",+ "encodings like ISO-8859 and are no longer needed since nowadays darcs does",+ "the right thing here by default.", "",- "There are two environment variables you can set to explicitly tell darcs",- "to not escape or escape octets. They are DARCS_DONT_ESCAPE_EXTRA and",- "DARCS_ESCAPE_EXTRA. Their values should be strings consisting of the",- "verbatim octets in question. The do-escapes take precedence over the",- "dont-escapes. Space characters are still escaped at line endings though.",- "The special environment variable DARCS_DONT_ESCAPE_TRAILING_CR turns off",- "escaping of carriage return last on the line (DOS style)."])+ "Finally, if you are in a highly security sensitive situation (or just",+ "paranoid for other reasons), you can set DARCS_ESCAPE_8BIT to 1. This will",+ "cause darcs to escape every non-ASCII byte in addition to ASCII control",+ "characters."])
@@ -1,6 +1,3 @@-{-# LANGUAGE CPP #-}-- -- | -- Module : Darcs.Util.Progress -- Copyright : 2008 David Roundy@@ -30,7 +27,7 @@ import Prelude ()-import Darcs.Prelude hiding ( lookup )+import Darcs.Prelude import Prelude hiding (lookup)
@@ -50,7 +50,7 @@ import Darcs.Util.SignalHandler ( catchNonSignal ) import Darcs.Util.URL ( SshFilePath, sshFilePathOf, sshUhost, sshRepo, sshFile )-import Darcs.Util.Text ( breakCommand )+import Darcs.Util.Text ( breakCommand, showCommandLine ) import Darcs.Util.Exception ( prettyException, catchall ) import Darcs.Util.Exec ( readInteractiveProcess, ExecException(..), Redirect(AsIs) ) import Darcs.Util.Progress ( withoutProgress, debugMessage, debugFail )@@ -177,9 +177,9 @@ 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)+ let sshargs = sshargs_ ++ ["--", sshUhost sshfp, rdarcs,+ "transfer-mode", "--repodir", sshRepo sshfp]+ debugMessage $ "Exec: " ++ showCommandLine (sshcmd:sshargs) (i,o,e,_) <- runInteractiveProcess sshcmd sshargs Nothing Nothing do hSetBinaryMode i True@@ -245,7 +245,8 @@ -- 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]+ let scp_args = filter (/="-q") args ++ ["--", u, dest]+ debugMessage $ "Exec: " ++ showCommandLine (scpcmd:scp_args) (r, scp_err) <- readInteractiveProcess scpcmd scp_args unless (r == ExitSuccess) $ throwIO $ ExecException scpcmd scp_args (AsIs,AsIs,AsIs) scp_err
@@ -13,6 +13,7 @@ , breakCommand , quote , pathlist+ , showCommandLine ) where import Prelude ()@@ -21,7 +22,7 @@ import Control.Arrow ( first ) import Data.List ( intercalate ) -import Darcs.Util.Printer ( Doc, (<>), renderString, RenderMode(..), quoted, hsep )+import Darcs.Util.Printer ( Doc, (<>), renderString, quoted, hsep ) sentence :: Doc -> Doc sentence = (<> ".")@@ -56,7 +57,7 @@ -- | Quote a string for screen output. quote :: String -> String-quote = renderString Encode . quoted+quote = renderString . 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@@ -64,3 +65,10 @@ -- another shell command. pathlist :: [FilePath] -> Doc pathlist paths = hsep (map quoted paths)++-- | Produce a String composed by the elements of [String] each enclosed in+-- double quotes.+showCommandLine :: [String] -> String+showCommandLine strings = showCommandLine' ['"'] strings+ where showCommandLine' x xs =+ x ++ intercalate (x ++ " " ++ x) xs ++ x
@@ -2,7 +2,6 @@ -- -- BSD3 {-# LANGUAGE ScopedTypeVariables, MultiParamTypeClasses, FlexibleInstances, BangPatterns #-}-{-# LANGUAGE CPP #-} -- | The abstract representation of a Tree and useful abstract utilities to -- handle those.@@ -32,13 +31,15 @@ , modifyTree, updateTree, partiallyUpdateTree, updateSubtrees, overlay , addMissingHashes ) where +import Prelude ()+import Darcs.Prelude hiding ( filter )+ 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.ByteString.Lazy as BL+import qualified Data.ByteString as B import qualified Data.Map as M import Data.Maybe( catMaybes, isNothing )@@ -47,8 +48,6 @@ import Control.Monad( filterM ) import Control.Applicative( (<$>) ) -#include "impossible.h"- -------------------------------- -- Tree, Blob and friends --@@ -94,7 +93,7 @@ itemType (SubTree _) = TreeType itemType (Stub _ _) = TreeType -emptyTree :: (Monad m) => Tree m+emptyTree :: Tree m emptyTree = Tree { items = M.empty , treeHash = NoHash } @@ -104,14 +103,14 @@ makeBlob :: (Monad m) => BL.ByteString -> Blob m makeBlob str = Blob (return str) (sha256 str) -makeBlobBS :: (Monad m) => BS.ByteString -> Blob m+makeBlobBS :: (Monad m) => B.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 :: [(Name,TreeItem m)] -> Tree m makeTree l = Tree { items = M.fromList l , treeHash = NoHash } -makeTreeWithHash :: (Monad m) => [(Name,TreeItem m)] -> Hash -> Tree m+makeTreeWithHash :: [(Name,TreeItem m)] -> Hash -> Tree m makeTreeWithHash l h = Tree { items = M.fromList l , treeHash = h } @@ -255,7 +254,7 @@ -- | 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 :: (FilterTree t m) => Tree n -> t m -> t m restrict guide tree = filter accept tree where accept path item = case (find guide path, item) of@@ -312,7 +311,7 @@ -- 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 :: forall 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)@@ -398,7 +397,7 @@ 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+countmap = M.foldr (\_ i -> i + 1) 0 updateSubtrees :: (Tree m -> Tree m) -> Tree m -> Tree m updateSubtrees fun t =@@ -409,11 +408,11 @@ 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 :: (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))+partiallyUpdateTree :: (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@@ -433,7 +432,7 @@ -- 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 :: (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 ]@@ -448,7 +447,7 @@ (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 :: (Monad 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)) =
@@ -36,10 +36,10 @@ 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.Char8 as BC+import qualified Data.ByteString.Lazy.Char8 as BLC import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString as BS+import qualified Data.ByteString as B import Data.List( sortBy ) import Data.Maybe( fromJust, isJust )@@ -48,6 +48,7 @@ import Darcs.Util.Path import Darcs.Util.ByteString ( FileSegment, readSegment ) import Darcs.Util.Hash+import Darcs.Util.Progress ( debugMessage ) import Darcs.Util.Tree import Darcs.Util.Tree.Monad @@ -55,18 +56,15 @@ -- 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+decodeDarcsHash :: BC.ByteString -> Hash+decodeDarcsHash bs = case BC.split '-' bs of+ [s, h] | BC.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+decodeDarcsSize :: BC.ByteString -> Maybe Int+decodeDarcsSize bs = case BC.split '-' bs of+ [s, _] | BC.length s == 10 ->+ case reads (BC.unpack s) of [(x, _)] -> Just x _ -> Nothing _ -> Nothing@@ -78,41 +76,41 @@ 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)+ hash = BC.unpack (encodeBase16 h) ---------------------------------------------- -- Darcs directory format. -- -darcsFormatDir :: Tree m -> Maybe BL8.ByteString-darcsFormatDir t = BL8.fromChunks . concat <$>+darcsFormatDir :: Tree m -> Maybe BLC.ByteString+darcsFormatDir t = BLC.fromChunks . concat <$> mapM string (sortBy cmp $ listImmediate t)- where cmp (Name a, _) (Name b, _) = compare a b- string (Name name, item) =+ where cmp (a, _) (b, _) = compare a b+ string (name, item) = do header <- case item of- File _ -> Just $ BS8.pack "file:\n"- _ -> Just $ BS8.pack "directory:\n"+ File _ -> Just $ BC.pack "file:\n"+ _ -> Just $ BC.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' ]+ , encodeWhiteName name+ , BC.singleton '\n'+ , hash, BC.singleton '\n' ] -darcsParseDir :: BL8.ByteString -> [(ItemType, Name, Maybe Int, Hash)]-darcsParseDir content = parse (BL8.split '\n' content)+darcsParseDir :: BLC.ByteString -> [(ItemType, Name, Maybe Int, Hash)]+darcsParseDir content = parse (BLC.split '\n' content) where parse (t:n:h':r) = (header t,- Name $ BS8.pack $ decodeWhite (BL8.unpack n),+ decodeWhiteName $ B.concat $ BL.toChunks n, decodeDarcsSize hash, decodeDarcsHash hash) : parse r- where hash = BS8.concat $ BL8.toChunks h'+ where hash = BC.concat $ BLC.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+ | x == BLC.pack "file:" = BlobType+ | x == BLC.pack "directory:" = TreeType+ | otherwise = error $ "Error parsing darcs hashed dir: " ++ BLC.unpack x ---------------------------------------- -- Utilities.@@ -130,7 +128,7 @@ darcsUpdateDirHashes = updateSubtrees update where update t = t { treeHash = darcsTreeHash t } -darcsUpdateHashes :: (Monad m, Functor m) => Tree m -> m (Tree m)+darcsUpdateHashes :: (Monad m) => Tree m -> m (Tree m) darcsUpdateHashes = updateTree update where update (SubTree t) = return . SubTree $ t { treeHash = darcsTreeHash t } update (File blob@(Blob con _)) =@@ -138,12 +136,12 @@ return $ File (Blob con hash) update stub = return stub -darcsHash :: (Monad m, Functor m) => TreeItem m -> m Hash+darcsHash :: (Monad 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 :: (Monad m) => Tree m -> m (Tree m) darcsAddMissingHashes = addMissingHashes darcsHash -------------------------------------------@@ -154,11 +152,12 @@ -- and with a given @hash@. readDarcsHashedDir :: FilePath -> (Maybe Int, Hash) -> IO [(ItemType, Name, Maybe Int, Hash)] readDarcsHashedDir dir h = do+ debugMessage $ "readDarcsHashedDir: " ++ dir ++ " " ++ BC.unpack (encodeBase16 (snd h)) 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+ return $ if BLC.null compressed then [] else darcsParseDir content @@ -201,7 +200,7 @@ _ <- mapM (dump . fromJust) dirs return $ darcsTreeHash t where dump bits =- do let name = dir </> BS8.unpack (encodeBase16 $ sha256 bits)+ do let name = dir </> BC.unpack (encodeBase16 $ sha256 bits) exist <- doesFileExist name unless exist $ BL.writeFile name (compress bits) @@ -209,7 +208,7 @@ -- 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 :: FilePath -> BLC.ByteString -> TreeIO () fsCreateHashedFile fn content = liftIO $ do exist <- doesFileExist fn@@ -230,16 +229,16 @@ updateFile b@(Blob _ !h) = do content <- liftIO $ readBlob b- let fn = dir </> BS8.unpack (encodeBase16 h)+ let fn = dir </> BC.unpack (encodeBase16 h) nblob = Blob (decompress <$> rblob) h- rblob = BL.fromChunks . return <$> BS.readFile fn+ rblob = BL.fromChunks . return <$> B.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)+ fn = dir </> BC.unpack (encodeBase16 hash) fsCreateHashedFile fn (compress dirdata) return s
@@ -1,7 +1,7 @@ -- Copyright (C) 2009-2011 Petr Rockai -- -- BSD3-{-# LANGUAGE ScopedTypeVariables, TypeSynonymInstances, UndecidableInstances, FlexibleInstances #-}+{-# LANGUAGE ScopedTypeVariables, TypeSynonymInstances, UndecidableInstances #-} -- | 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@@ -32,7 +32,7 @@ import Data.Int( Int64 ) import Data.Maybe( isNothing, isJust ) -import qualified Data.ByteString.Lazy.Char8 as BL+import qualified Data.ByteString.Lazy as BL import Control.Monad.RWS.Strict import qualified Data.Map as M @@ -88,18 +88,18 @@ , maxage = 0 , update = u } -flush :: (Functor m, Monad m) => TreeMonad m ()+flush :: (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' :: (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 :: (Monad m) => TreeMonad m a -> TreeState m -> m (a, Tree m) runTreeMonad action initial = do let action' = do x <- action flush@@ -111,7 +111,7 @@ -- 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 :: (Monad m) => TreeMonad m a -> Tree m -> m (a, Tree m) virtualTreeMonad action t = runTreeMonad' action $ initialState t (\_ -> return NoHash) (\_ x -> return x) @@ -121,7 +121,7 @@ -- | 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)+modifyItem :: (Monad m) => AnchoredPath -> Maybe (TreeItem m) -> TreeMonad m () modifyItem path item = do path' <- (`catPaths` path) `fmap` currentDirectory@@ -139,7 +139,7 @@ , maxage = age + 1 , changesize = changesize st + change } -renameChanged :: (Functor m, Monad m)+renameChanged :: (Monad m) => AnchoredPath -> AnchoredPath -> TreeMonad m () renameChanged from to = modify $ \st -> st { changed = rename' $ changed st } where rename' = M.fromList . map renameone . M.toList@@ -152,13 +152,13 @@ -- '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)+replaceItem :: (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 :: forall m. (Monad m) => AnchoredPath -> TreeMonad m () flushItem path = do current <- gets tree case find current path of@@ -176,7 +176,7 @@ -- | If buffers are becoming large, sync, otherwise do nothing.-flushSome :: (Monad m, Functor m) => TreeMonad m ()+flushSome :: (Monad m) => TreeMonad m () flushSome = do x <- gets changesize when (x > megs 100) $ do remaining <- go =<< sortBy age . M.toList <$> gets changed@@ -191,7 +191,7 @@ megs = (* (1024 * 1024)) age (_, (_, a)) (_, (_, b)) = compare a b -instance (Functor m, Monad m) => TreeRO (TreeMonad m) where+instance (Monad m) => TreeRO (TreeMonad m) where expandTo p = do t <- gets tree p' <- (`catPaths` p) `fmap` ask@@ -224,7 +224,7 @@ dir' <- expandTo dir local (const dir') act -instance (Functor m, Monad m) => TreeRW (TreeMonad m) where+instance (Monad m) => TreeRW (TreeMonad m) where writeFile p con = do _ <- expandTo p modifyItem p (Just blob)@@ -264,17 +264,17 @@ let item = find tr from' unless (isNothing item) $ modifyItem to item -findM' :: forall m a. (Monad m, Functor m)+findM' :: forall m a . (Monad 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 :: (Monad m) => Tree m -> AnchoredPath -> m (Maybe (TreeItem m)) findM = findM' find -findTreeM :: (Monad m, Functor m) => Tree m -> AnchoredPath -> m (Maybe (Tree m))+findTreeM :: (Monad m) => Tree m -> AnchoredPath -> m (Maybe (Tree m)) findTreeM = findM' findTree -findFileM :: (Monad m, Functor m) => Tree m -> AnchoredPath -> m (Maybe (Blob m))+findFileM :: (Monad m) => Tree m -> AnchoredPath -> m (Maybe (Blob m)) findFileM = findM' findFile
@@ -26,12 +26,12 @@ ) 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 System.Posix.Files+ ( getSymbolicLinkStatus, isDirectory, isRegularFile, FileStatus ) import Darcs.Util.Path import Darcs.Util.File ( withCurrentDirectory )@@ -45,7 +45,7 @@ readPlainDir dir = withCurrentDirectory dir $ do items <- getDirectoryContents "."- sequence [ do st <- getFileStatus s+ sequence [ do st <- getSymbolicLinkStatus s return (s, st) | s <- items, s `notElem` [ ".", ".." ] ] @@ -53,14 +53,14 @@ readPlainTree dir = do items <- readPlainDir dir let subs = catMaybes [- let name = Name (BS8.pack name')+ let name = makeName name' in case status of _ | isDirectory status -> Just (name, Stub (readPlainTree (dir </> name')) NoHash)- _ | isRegularFile status -> Just (name, File $ Blob (readBlob' 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)+ where readBlob' name = readSegment (dir </> 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".
@@ -1,5 +1,3 @@-{-# LANGUAGE CPP #-}- {- Copyright (C) 2004 David Roundy @@ -63,8 +61,6 @@ import Data.Char ( isSpace ) import qualified System.FilePath as FP ( isRelative, isAbsolute, isValid ) import System.FilePath ( (</>) )--#include "impossible.h" isRelative :: String -> Bool isRelative "" = bug "Empty filename in isRelative"
@@ -1,69 +0,0 @@-/*- * Copyright (C) 2003 David Roundy- * Most of the UTF code is Copyright (C) 1999-2001 Free Software Foundation, Inc.- * This file is part of darcs.- *- * Darcs is free software; you can redistribute it and/or modify it under- * the terms of the GNU Library General Public License as published by the- * Free Software Foundation; either version 2 of the License, or (at your- * option) any later version.- *- * You should have received a copy of the GNU Library General Public- * License along with the GNU LIBICONV Library; see the file COPYING.LIB.- * If not, write to the Free Software Foundation, Inc., 51 Franklin Street,- * Fifth Floor, Boston, MA 02110-1301, USA.- *- */-#include "fpstring.h"--#include <stdlib.h>-#include <string.h>-#include <stdio.h>---#ifdef _WIN32-#include <windows.h>-#else-#include <sys/mman.h>-#endif--int has_funky_char(const char *s, int len)-{- // We check first for the more likely \0 so we can break out of- // memchr that much sooner.- return !!(memchr(s, 0, len) || memchr(s, 26, len));--}--/* Conversion to and from hex */--void conv_to_hex(unsigned char *dest, unsigned char *from, int num_chars)-{- static char hex[] = "0123456789abcdef";- unsigned char *end;-- for (end = from + num_chars; from < end; from++) {- *dest++ = hex[*from >> 4];- *dest++ = hex[*from & 0xf];- }-- return;-}--#define NYBBLE_TO_INT(c) \- ((c) - ((c) >= 'a' ? 'a' - 10 : '0'))--void conv_from_hex(unsigned char *dest, unsigned char *from, int num_chars)-{- unsigned char *end;- unsigned char c;-- end = dest + num_chars;- while (dest < end) {- c = NYBBLE_TO_INT(*from) << 4, from++;- *dest++ = c | NYBBLE_TO_INT(*from), from++;- }-- return;-}-
@@ -1,7 +0,0 @@-#include <HsFFI.h>-#include <sys/types.h>--int has_funky_char(const char *s, int len);--void conv_to_hex(unsigned char *dest, unsigned char *from, int num_chars);-void conv_from_hex(unsigned char *dest, unsigned char *from, int num_chars);
@@ -1,18 +0,0 @@-#include "h_iconv.h"--// Wrapper functions, since iconv_open et al are macros in libiconv.-iconv_t darcs_iconv_open(const char *tocode, const char *fromcode) {- return iconv_open(tocode, fromcode);-}--void darcs_iconv_close(iconv_t cd) {- iconv_close(cd);-}--size_t darcs_iconv(iconv_t cd, char **inbuf, size_t *inbytesleft,- char **outbuf, size_t *outbytesleft) {- // Cast inbuf to (void*) so that it works both on Solaris, which expects- // a (const char**), and on other platforms (e.g. Linux), which expect- // a (char **).- return iconv(cd, (void*)inbuf, inbytesleft, outbuf, outbytesleft);-}
@@ -1,9 +0,0 @@-#include <iconv.h>--iconv_t darcs_iconv_open(const char *tocode, const char *fromcode);--void darcs_iconv_close(iconv_t cd);--size_t darcs_iconv(iconv_t cd, char **inbuf, size_t *inbytesleft,- char **outbuf, size_t *outbytesleft);-
@@ -1,8 +0,0 @@-import qualified Darcs.Util.Bug as Bug_--#define darcsBug (\imp_funny_name -> imp_funny_name (__FILE__,__LINE__,__TIME__,__DATE__))--#define bug (darcsBug Bug_._bug)-#define impossible (darcsBug Bug_._impossible)-#define fromJust (darcsBug Bug_._fromJust)-#define bugDoc (darcsBug Bug_._bugDoc)
@@ -1,15 +1,18 @@ {-# LANGUAGE CPP, ForeignFunctionInterface #-}-module System.Posix.Files( isNamedPipe, isDirectory, isRegularFile, isSymbolicLink- , getFdStatus, getFileStatus, getSymbolicLinkStatus- , modificationTime, setFileMode, fileSize, fileMode- , stdFileMode, linkCount, createLink- , FileStatus- ) where+module System.Posix.Files+ ( isNamedPipe, isDirectory, isRegularFile, isSymbolicLink+ , getFdStatus, getFileStatus, getSymbolicLinkStatus+ , modificationTime, setFileMode, fileSize, fileMode+ , stdFileMode, FileStatus, fileID+ , linkCount, createLink+ ) where -import System.PosixCompat.Files( isNamedPipe, isDirectory, isRegularFile, isSymbolicLink- , getFdStatus, getFileStatus, getSymbolicLinkStatus- , modificationTime, setFileMode, fileSize, fileMode- , stdFileMode, FileStatus )+import System.PosixCompat.Files+ ( isNamedPipe, isDirectory, isRegularFile, isSymbolicLink+ , getFdStatus, getFileStatus, getSymbolicLinkStatus+ , modificationTime, setFileMode, fileSize, fileMode+ , stdFileMode, FileStatus, fileID+ ) import Foreign.C.String( CWString, withCWString ) import Foreign.C.Error( throwErrnoPathIf_ )
@@ -1,7 +1,7 @@ {-# LANGUAGE ForeignFunctionInterface #-} module System.Posix.IO where -#if mingw32_HOST_OS && __GLASGOW_HASKELL__ >= 612+#if mingw32_HOST_OS import Foreign.C.String( withCWString ) #else import Foreign.C.String ( withCString )
@@ -2,9 +2,8 @@ . ./lib rm -rf temp1-mkdir temp1+darcs init temp1 cd temp1-darcs init touch foo bar darcs add foo bar @@ -38,6 +37,17 @@ darcs record -a -m add_foo -A x darcs remove foo darcs record -a -m del_foo -A x+cd ..+rm -rf temp1++# issue1162: add nonexistent slash++rm -rf temp1+darcs init temp1+cd temp1+not darcs add a/ 2> err+cat err+grep 'File a does not exist!' err cd .. rm -rf temp1
@@ -3,10 +3,9 @@ # issue701-rm -rf temp0 temp1 temp2 # step 1-darcs init temp0 --darcs-2+darcs init temp0 cd temp0 echo m1 > foo darcs record -lam m1@@ -59,5 +58,3 @@ echo m2-a1-m4 > foo echo y | darcs mark-conflicts cd ..--rm -rf temp0 temp1 temp2
@@ -198,7 +198,6 @@ not grep '^ \* d' log # does not complain about an unrelated patch grep '^ \* y' log # complains about the offending one instead -cd .. rm -rf R S ## Test that apply --skip-conflicts filters the conflicts
@@ -2,7 +2,6 @@ . lib -rm -rf temp1 darcs init temp1 cd temp1 touch t.t@@ -11,11 +10,10 @@ DIR=`pwd` abs_to_context="${DIR}/my_context" cd ..-rm -rf temp2+ darcs clone temp1 --context="${abs_to_context}" temp2 darcs log --context --repo temp2 > repo2_context diff -u "${abs_to_context}" repo2_context-cd .. rm -rf temp1 temp2 # issue1865: cover interaction of clone --context with tags@@ -30,6 +28,7 @@ darcs log --context > my_context abs_to_context="$(pwd)/my_context" cd ..+ darcs clone temp1 --context="${abs_to_context}" temp2 darcs log --context --repo temp2 > repo2_context diff -u "${abs_to_context}" repo2_context@@ -69,8 +68,8 @@ cd temp3 darcs log | not grep wobble cd ..-rm -rf temp1 temp2 temp3 +rm -rf temp1 temp2 temp3 # issue885: darcs clone --to-match @@ -81,20 +80,18 @@ firsthash=`darcs log --xml | grep 'hash=' | sed -e "s/.*hash='//" -e "s/'>//"` echo second > b darcs record -lam 'second'- cd ..-darcs init temp2-cd temp2-darcs pull -a --match "hash $firsthash" ../temp1- # pulling that patch works ok-cd ..-darcs clone --to-match "hash $firsthash" temp1 temp3- # cloning up to that patch does not++darcs clone --to-match "hash $firsthash" temp1 temp2+test $(darcs log --count --repodir temp2) -eq 1++darcs clone --to-hash $firsthash temp1 temp3+test $(darcs log --count --repodir temp3) -eq 1+ rm -rf temp1 temp2 temp3 # various tests for clone --tag -rm -rf temp1 temp2 darcs init temp1 cd temp1 echo ALL ignore-times >> _darcs/prefs/defaults@@ -112,8 +109,8 @@ darcs record -am EE echo F > foo darcs record -am FF- cd ..+ darcs clone --tag 1.0 --repo-name temp2 temp1 cmp temp2/foo temp1/foo_version_1.0 rm -rf temp1 temp2 temp3@@ -141,16 +138,19 @@ EOF darcs rec -alm 'Remove line 3' cd ..+ darcs init temp2 cd temp2 echo ynyy | darcs pull ../temp1 darcs tag -m Tag darcs push -a ../temp1 cd ..+ darcs clone --tag=Tag temp1 temp3 cd temp3 darcs check cd ..+ rm -rf temp1 temp2 temp3 # clone --tag : check that pending looks ok@@ -163,6 +163,7 @@ rmdir d darcs rec -am 'rm d' cd ..+ darcs clone --tag t temp1 temp2 cd temp2 if [ -f _darcs/patches/pending ]; then@@ -172,8 +173,8 @@ fi fi cd ..-rm -rf temp1 temp2 +rm -rf temp1 temp2 # issue2230 - darcs clone --context checks the validity of the context # file too late.
@@ -1,7 +1,5 @@ #!/usr/bin/env bash -#pragma repo-format darcs-1,darcs-2- . lib # Tests for the doppleganger conflict bug.
@@ -2,12 +2,10 @@ . ./lib -rm -rf temp0 temp1 temp2- # step 1 mkdir temp0 cd temp0-darcs init --darcs-2+darcs init echo temp0 > _darcs/prefs/author echo m1 > foo darcs add foo@@ -73,6 +71,3 @@ cd temp1 darcs pull -av ../temp2 cd ..--rm -rf temp0 temp1 temp2-
@@ -27,11 +27,8 @@ ## 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
@@ -0,0 +1,64 @@+#!/usr/bin/env bash++## Tests for convert command based on previously checked results+## to generate new test material for this test,+## see bin/convert-writer.sh+##+## 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++skip-formats darcs-1++runtest() {+ opt=$1+ name=$2+ rm -rf temp+ mkdir temp+ cd temp++ mkdir repo+ cd repo+ darcs init --darcs-1+ darcs apply --allow-conflicts $TESTDATA/convert/darcs1/$name.dpatch+ cd ..+ echo 'I understand the consequences of my action' | darcs convert darcs-2 repo repo2 $opt+ mkdir empty-darcs2+ cd empty-darcs2+ darcs init --darcs-2+ cd ..+ cd repo2+ darcs send --no-minimize -a -o ../$name-darcs2.dpatch ../empty-darcs2+ cd ..+ diff -I'1 patch for repository ' -I'patches for repository ' -I'Oct 1' -u $TESTDATA/convert/darcs2/$name.dpatch $name-darcs2.dpatch+}++for opt in --no-working-dir --with-working-dir; do+ runtest $opt simple+ runtest $opt twowayconflict+ runtest $opt threewayconflict+ runtest $opt threewayanddep+ runtest $opt threewayandmultideps+ runtest $opt resolution+ runtest $opt tworesolutions+done
@@ -1,62 +0,0 @@-#!/usr/bin/env bash--#pragma repo-format darcs-1--## Tests for convert command based on previously checked results-## to generate new test material for this test,-## see bin/convert-writer.sh-##-## 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--grep darcs-1 $HOME/.darcs/defaults || exit 200--runtest() {- rm -rf temp- mkdir temp- cd temp-- mkdir repo- cd repo- darcs init --darcs-1- darcs apply --allow-conflicts $TESTDATA/convert/darcs1/$1.dpatch- cd ..- echo 'I understand the consequences of my action' | darcs convert darcs-2 repo repo2- mkdir empty-darcs2- cd empty-darcs2- darcs init --darcs-2- cd ..- cd repo2- darcs send --no-minimize -a -o ../$1-darcs2.dpatch ../empty-darcs2- cd ..- diff -I'1 patch for repository ' -I'patches for repository ' -I'Oct 1' -u $TESTDATA/convert/darcs2/$1.dpatch $1-darcs2.dpatch-}--runtest simple-runtest twowayconflict-runtest threewayconflict-runtest threewayanddep-runtest threewayandmultideps-runtest resolution-runtest tworesolutions
@@ -34,6 +34,12 @@ echo 'Example content.' > d/f darcs record -lam 'Add d/f and e.' +# one more change, this time with non-ASCII file name, comment, and content+# as well as a space in the file name+# see http://bugs.darcs.net/issue2359+echo 'Liebe Grüße' > e/'Liebe Grüße'+darcs record -lam 'Add file e/"Liebe Grüße"'+ git init gitmirror darcs convert export --write-marks darcs-to-git.marks > fex@@ -49,3 +55,4 @@ git clone gitmirror gitmirror-clone diff e/f gitmirror-clone/e/f+diff e/"Liebe Grüße" gitmirror-clone/e/"Liebe Grüße"
binary file changed (250790 → absent bytes)
binary file changed (169112 → absent bytes)
@@ -1,6 +1,9 @@ #!/usr/bin/env bash . ./lib +export DARCS_TMPDIR=`pwd`/tmp+mkdir tmp+ rm -rf temp1 darcs init temp1 cd temp1
@@ -1,23 +0,0 @@-#!/usr/bin/env bash-. ./lib--T=temp-rm -rf "$T"-mkdir "$T"-echo "$T"-cd "$T"--darcs initialize-echo text > afile.txt-darcs add afile.txt-darcs record --author me --all --no-test --name init-mkdir d-darcs add d-mkdir d/e-darcs add d/e-darcs mv afile.txt d/e/afile.txt-echo altered_text > d/e/afile.txt-darcs record --author me --all --no-test --name confusion-test ! -f _darcs/pristine/afile.txt-echo y/d/y | tr / \\012 | darcs unrecord-rm -rf "$T"
@@ -13,7 +13,7 @@ grep disable log rm log # --disable works from defaults- sub_commands="$(darcs $cmd --list-options | grep -v -- -- || true)"+ sub_commands="$(darcs $cmd --list-options | grep -v -- -- | cut -f ';' -f 1 || true)" # disabling super commands in the defaults file is broken if test -z "$sub_commands"; then echo "$cmd --disable" > _darcs/prefs/defaults
@@ -4,7 +4,7 @@ # Set up a base repo. Our experiment will start from this point mkdir base cd base-darcs init --darcs-2+darcs init printf "Line1\nLine2\nLine3\n" > foo darcs rec -alm Base cd ..
@@ -1,62 +0,0 @@-#!/usr/bin/env bash-## Test for issue1363 - mark-conflicts should report that there is a-## conflict to mark if apply/push say there are-##-## Copyright (C) 2010 Eric Kow-## Copyright (C) 2009 Thorkil Naur-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-## SOFTWARE.--. lib # Load some portability helpers.-rm -rf R S T # Another script may have left a mess.-darcs init --repo R # Create our test repos.-darcs init --repo S-darcs init --repo T--cd R- touch f.txt- darcs add f.txt- darcs record -am "Empty f.txt"- echo f.txt contents >> f.txt- darcs record -am "Contents of f.txt"-cd ..--cd S- echo yn | darcs pull ../R- rm f.txt- darcs record -am 'Remove f.txt in S'-cd ..--cd T- echo ynn | darcs pull ../R- rm f.txt- darcs record -am 'Remove f.txt in T'- not darcs push -a ../R > log # should fail because of conflict- grep "There are conflicts" log-cd ..--cd R- darcs pull -a ../S- darcs revert -a- darcs pull -a ../T- echo y | darcs mark-conflicts > log- not grep "No conflicts" log-cd ..
@@ -25,14 +25,8 @@ ## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## SOFTWARE. -#pragma repo-format darcs-2- . lib -## This bug only affects darcs-2 repositories.-fgrep darcs-2 ~/.darcs/defaults &>/dev/null || exit 200--rm -rf d e # Another script may have left a mess. darcs initialize --repodir d/ darcs initialize --repodir e/ touch d/f d/g e/f@@ -40,7 +34,6 @@ darcs record --repodir e/ -lam 'Add f' echo >d/f darcs record --repodir d/ -am 'Change f'-darcs pull --repodir e/ -a d/-darcs obliterate --repodir e/ -ap 'Add f and g'+darcs pull --repodir e/ -a d/ --allow-conflicts #no conflict mark-up+echo y | darcs obliterate --repodir e/ -ap 'Add f and g' darcs pull --repodir e/ -a d/-rm -rf d/ e/ # Clean up after ourselves.
@@ -25,12 +25,9 @@ ## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## SOFTWARE. -#pragma repo-format darcs-1- . lib # Load some portability helpers. -# this test is not relevant for darcs 2 repositories-(not grep darcs-2 $HOME/.darcs/defaults) || exit 200+# this test fails only for darcs 2 repositories rm -rf S1 S2 S3 # Another script may have left a mess. darcs init --repo S1 # Create our test repos.
@@ -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
@@ -1,53 +0,0 @@-#!/usr/bin/env bash-## Test for issue2208 - darcs shouldn't fail to replace if unrecorded changes-## would make the replace succeed-##-## Copyright (C) 2012 Owen Stephens-##-## Permission is hereby granted, free of charge, to any person-## obtaining a copy of this software and associated documentation-## files (the "Software"), to deal in the Software without-## restriction, including without limitation the rights to use, copy,-## modify, merge, publish, distribute, sublicense, and/or sell copies-## of the Software, and to permit persons to whom the Software is-## furnished to do so, subject to the following conditions:-##-## The above copyright notice and this permission notice shall be-## included in all copies or substantial portions of the Software.-##-## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,-## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF-## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND-## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS-## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN-## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN-## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE-## SOFTWARE.--. lib--rm -rf R--darcs init --repo R--cd R--echo -e 'foo\nbar' > testing--darcs rec -alm 'Add testing file'--echo -e 'baz\nbar' > testing--# Darcs will complain here, since we've not recorded the fact that we've-# removed the occurrence of foo-darcs replace bar foo testing | not grep Skipping--# We can get around this by recording, then replacing and amending the patch...-darcs rec -am "I don't want to have to record this!"--darcs replace bar foo testing--echo y | darcs amend -a--# Check the workaround succeeded.-darcs changes --last 1 -v | grep 'replace.*bar.*foo'
@@ -1,37 +0,0 @@-#!/usr/bin/env bash-## Test for issue2243 - annotating a file with a blank first gives an "unknown"-## patch.-##-## 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 --darcs init --repo R--cd R--echo -e "\nline2" > file--darcs rec -alm 'Add file'--darcs annotate file | not grep unknown
@@ -0,0 +1,45 @@+#!/usr/bin/env bash+## Test for issue2275 - darcs follows symbolic links instead of properly+## ignoring them.+## When substituting a recorded file with a symbolic link, darcs becomes+## confused and associates the filename label to the content of the file+## pointed by the link.+##+## Copyright (C) 2017 Gian Piero Carrubba+##+## 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.+abort_windows # Skip test on Windows++darcs init --repo R # Create the test repo.+cd R++touch g # Change the working tree.+echo 'This line should not appear in g.' > f+darcs record -lam 'Add f and g.'++rm -f g # Remove g and create a link with the+ln -s f g # same name ponting to f++darcs diff g | not grep -F '+This line should not appear in g.'++cd ..
@@ -0,0 +1,35 @@+#!/usr/bin/env bash+## Test for issue2443 - timestamp index keeps information about files+## added by unrecorded patches+##+## Copyright (C) 2017 Guillaume Hoffmann+##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+## SOFTWARE.++. lib # Load some portability helpers.+darcs init R # Create our test repos.+cd R+touch a b c # add some files+darcs record -lam "Add a b c"+touch d+darcs record -lam "Add d" # add another file+echo yd | darcs unrecord+not darcs whatsnew # d is not supposed to be tracked
@@ -1,9 +0,0 @@-#!/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 './äöüßÄÖÜ'
@@ -0,0 +1,43 @@+. lib++darcs init r1+cd r1+touch f+darcs record -lam 'added f as file'+cd ..++darcs init r2+cd r2+mkdir f+darcs record -lam 'added f as dir'++darcs pull -a ../r1++# darcs-1 and darcs-2 resolve the conflict+# differently, we allow both+if cd f && cd ..; then+ rmdir f+else+ rm f+fi+mv f.\~0\~ f+# darcs whatsnew at this point reports nothing+not grep . ../whatsnew+# so revert should do nothing+darcs revert -a++# at this point pending should definitely be empty+# and the following should fail (nothing to record)+if not darcs record -lam 'resolve conflict'; then+ exit 0;+else+ # check that what we record is at least consistent+ # i.e. we have either addfile or adddir for f, but not both+ darcs log -v --last=1 > ../log+ if grep 'addfile ./f' ../log; then+ not grep 'adddir ./f' ../log+ fi+ if grep 'adddir ./f' ../log; then+ not grep 'addfile ./f' ../log+ fi+fi
@@ -43,7 +43,7 @@ bar bar EOF-echo yyy | darcs amend-record --look-for-replaces+echo y | darcs amend-record --look-for-replaces -a cd .. rm -rf R
@@ -1,44 +0,0 @@-#!/usr/bin/env bash-## Failing test for --look-for-replaces combined with --look-for-moves-##-## Copyright (C) 2014 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--rm -rf R-mkdir R-cd R--darcs init-cat > file <<EOF-foo-EOF-darcs record -lam file-cat > file <<EOF-bar-EOF-mv file newfile-darcs whatsnew --look-for-moves --look-for-replaces | grep replace-cd ..-rm -rf R-
@@ -0,0 +1,10 @@+. lib++darcs init R+cd R++filename="file with spaces in its name"+echo xxx > "$filename"+darcs record -lam "comment"+darcs log -s | grep -c "$filename" | grep -w 1+darcs log -v | grep -c "$filename" | grep -w 2
@@ -31,7 +31,7 @@ mkdir temp1 cd temp1-darcs init --darcs-1+darcs init touch foo darcs add foo darcs rec -m t1 -a -A tester
@@ -2,10 +2,12 @@ . ./lib -rm -rf temp1 temp2+# this test fails for darcs-1 repos+skip-formats darcs-1+ mkdir temp1 cd temp1-darcs init --darcs-2+darcs init echo first > a darcs add a darcs record -am 'first'@@ -28,5 +30,3 @@ darcs pull -a ../temp2 | grep conflict grep third a cd ..--rm -rf temp1 temp2
@@ -1,16 +0,0 @@-#!/bin/sh--. ./lib--not () { "$@" && exit 1 || :; }--rm -rf temp-mkdir temp-cd temp-darcs init-not darcs add a/ 2> err-cat err-grep 'File a does not exist!' err-cd ..--rm -rf temp
@@ -58,14 +58,14 @@ darcs add foo bar darcs record -a -m add_foo_bar -A x -# If --sendmail-command is provided, no warning-darcs send --author=me -a --to=random@random --sendmail-command='true' ../temp2-# If --dry-run is provided, no warning-darcs send --author=me -a --to=random@random --dry-run ../temp2-# If -o or -O is provided, no warning+# If --mail and --sendmail-command is provided, no warning+darcs send --mail --author=me -a --to=random@random --sendmail-command='true' ../temp2+# If --mail and --dry-run is provided, no warning+darcs send --mail --author=me -a --to=random@random --dry-run ../temp2+# If --mail is not provided, no warning darcs send --author=me -a --to=random@random -O ../temp2 darcs send --author=me -a --to=random@random -o test.patch ../temp2 # Otherwise, fail early-(darcs send --author=me -a --to=random@random ../temp2 || true) | grep "No working sendmail"+(darcs send --mail --author=me -a --to=random@random ../temp2 || true) | grep "No working sendmail" cd ..
@@ -50,7 +50,8 @@ # we want escaping, otherwise output of non-ASCII characters is unreliable export DARCS_DONT_ESCAPE_ANYTHING=0-export DARCS_DONT_ESCAPE_8BIT=0+# note default changed, DARCS_DONT_ESCAPE_8BIT is no longer recognized+export DARCS_ESCAPE_8BIT=1 export DARCS_DONT_ESCAPE_ISPRINT=0 rm -rf R@@ -61,7 +62,7 @@ echo garbelbolf > aargh darcs add aargh echo -e '\xe2\x80\x9e\x54\x61\x20\x4d\xc3\xa8\x72\x65\xe2\x80\x9d' > message.txt-darcs record --logfile=message.txt -A 'Petra Testa van der Test <test@example.com>' -a > rec.txt+darcs record --logfile=message.txt -a > rec.txt darcs changes > log.txt cat log.txt grep '<U+201E>' log.txt
@@ -1,7 +1,5 @@ #!/usr/bin/env bash -#pragma repo-format darcs-2- . lib mkdir future@@ -17,10 +15,6 @@ cat _darcs/format cd .. -if grep darcs-1 .darcs/defaults; then- exit 200-fi- # get future repo: should be ok darcs get future temp1 cd temp1@@ -29,5 +23,3 @@ darcs add toto darcs record -am 'blah' cd ..-rm -rf temp1 future-
@@ -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
@@ -0,0 +1,19 @@+# Off-by-one error when reporting patches that append an empty line to a file.+# An affected darcs would report 'hunk ./f 3' in the provided tests.+. lib++darcs init R+cd R+echo 'first line' > f+darcs record -lam 'first line'+echo '' >> f+darcs whatsnew > wh+darcs record -am 'appended empty line'+darcs log -v --last=1 > log+darcs annotate f > ann++# these currently all fail:+grep 'hunk ./f 2' wh+grep 'hunk ./f 2' log+not grep unknown ann+grep '#2' ann
@@ -0,0 +1,56 @@+#!/usr/bin/env bash+## Test for issue2208 - darcs shouldn't fail to replace if unrecorded changes+## would make the replace succeed+##+## Copyright (C) 2012 Owen Stephens+##+## Permission is hereby granted, free of charge, to any person+## obtaining a copy of this software and associated documentation+## files (the "Software"), to deal in the Software without+## restriction, including without limitation the rights to use, copy,+## modify, merge, publish, distribute, sublicense, and/or sell copies+## of the Software, and to permit persons to whom the Software is+## furnished to do so, subject to the following conditions:+##+## The above copyright notice and this permission notice shall be+## included in all copies or substantial portions of the Software.+##+## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS+## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN+## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN+## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+## SOFTWARE.++. lib++rm -rf R++darcs init --repo R++cd R++echo -e 'foo\nbar' > testing++darcs rec -alm 'Add testing file'++echo -e 'baz\nbar' > testing++# Darcs will complain here, since we've not recorded the fact that we've+# removed the occurrence of foo+darcs replace bar foo testing | not grep Skipping++cat <<EOF > ../expected+hunk ./testing 1+-foo++baz+replace ./testing [A-Za-z_0-9] bar foo+EOF++darcs whatsnew > ../actual++cd ..++diff actual expected
@@ -0,0 +1,37 @@+#!/usr/bin/env bash+## Test for issue2243 - annotating a file with a blank first gives an "unknown"+## patch.+##+## 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 ++darcs init --repo R++cd R++echo -e "\nline2" > file++darcs rec -alm 'Add file'++darcs annotate file | not grep unknown
@@ -0,0 +1,7 @@+#!/usr/bin/env bash++. lib+darcs init R+cd R+touch äöüßÄÖÜ+darcs whatsnew -l | grep './äöüßÄÖÜ'
@@ -26,9 +26,22 @@ . lib +gunzip -c $TESTDATA/metadata-encoding.tgz | tar xf -+cd metadata-encoding++darcs log -v+ switch_to_utf8_locale+darcs log -v+darcs clone . ../utf8-clone+darcs log -v --repodir ../utf8-clone -gunzip -c $TESTDATA/metadata-encoding.tgz | tar xf -+switch_to_latin9_locale+darcs log -v+darcs clone . ../latin-clone+darcs log -v --repodir ../latin-clone -cd metadata-encoding-darcs changes+LC_ALL=C+darcs log -v+darcs clone . ../c-clone+darcs log -v --repodir ../c-clone
@@ -27,7 +27,7 @@ function getRecordedChanges () { darcs rec -am 1 # Ignore patch details and unindent - we should have the same contents as wh-darcs changes --last 1 -v | tail -n+5 | sed -e 's/^\s\+//' > $1+darcs changes --last 1 -v | tail -n+5 | sed -e 's/^\s\+//' | grep -v '^[{}]$' > $1 darcs unrecord --last 1 -a }
@@ -0,0 +1,9 @@+#!/usr/bin/env bash++. lib+darcs init R+cd R+echo 'äöüßÄÖÜ' > file+darcs whatsnew -l --no-summary | grep '+äöüßÄÖÜ'+darcs record -lam'added file'+darcs log -v | grep '+äöüßÄÖÜ'
@@ -0,0 +1,47 @@+#!/usr/bin/env bash+## Test for issue2512 - Multiple authors in global config get overwritten++. lib+abort_windows # different directory names on Windows++# helper+function fail {+ echo "ERROR: $1"+ exit 1+}++# add multiple author IDs to global preferences+cat >"$HOME/.darcs/author" <<-EOF+ AUTHOR_1, this one will be chosen when prompted for an author+ AUTHOR_2, this one should still be there in the end+EOF++# create a repo+rm -rf repo+mkdir repo+cd repo+darcs init++# make a change so that we have something to commit+touch changed+darcs add changed++# darcs will find multiple authors in global preferences,+# so darcs will ask for the author and we pick the first one+darcs record -am 'testing' <<-EOF+ 1+EOF+echo++# check the configuration after the commit (primary test for this issue)+if ! grep -q 'AUTHOR_2' "$HOME/.darcs/author"; then+ fail "'\$HOME/.darcs/author' with multiple authors was clobbered."+fi++# in addition, confirm that the author was properly added to the repository+if ! [[ -f _darcs/prefs/author ]]; then+ fail "The author was not recorded in the repository."+fi+if ! grep -q 'AUTHOR_1' _darcs/prefs/author; then+ fail "An 'author' file was created in the repository, but does not contain the chosen author."+fi
@@ -0,0 +1,19 @@+#!/usr/bin/env bash++. lib++# test that 'whatsnew -l --boring' actually lists boring files++rm -rf R+darcs init R+cd R++echo xxx > boring+darcs setpref boringfile boring+darcs record -lam'added boring and set as boringile'+touch xxx+darcs whatsnew -l --boring | grep xxx+darcs whatsnew -l --boring | grep -v 'No changes'++cd ..+rm -rf R
@@ -0,0 +1,49 @@+#!/usr/bin/env bash+## Test for issue2545 - Argument smuggling in SSH repository URLs+## Darcs allows (almost) arbitrary command execution via a crafted ssh URI.+##+## Copyright (C) 2017 Gian Piero Carrubba+##+## 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++DARCS_SCP=sftp darcs pull -a ssh://-oProxyCommand='touch FAIL' \+ 2>/dev/null || true+not ls FAIL >/dev/null++DARCS_SCP=sftp darcs pull -a -- -oProxyCommand='touch FAIL':dir \+ 2>/dev/null || true+not ls FAIL >/dev/null++# Executing the same tests with `clone' instead of `pull'. The results shoud+# be the same, but better safe than sorry.+DARCS_SCP=sftp darcs clone ssh://-oProxyCommand='touch FAIL' S \+ 2>/dev/null || true+not ls FAIL >/dev/null++DARCS_SCP=sftp darcs clone -- -oProxyCommand='touch FAIL':dir T \+ 2>/dev/null || true+not ls FAIL >/dev/null++cd ..
@@ -0,0 +1,41 @@+#!/usr/bin/env bash++# check that darcs whatsnew --unified outputs correct context lines++. lib++cat > before << EOF+1+2+3+4+5+EOF++cat > after << EOF+1+3+4a+4b+5+EOF++cat > exp << EOF+hunk ./file 2+ 1+-2+ 3+hunk ./file 3+-4++4a++4b+ 5+EOF++darcs init R+cd R+cp ../before file+darcs record -lam 'add file'+cp ../after file+darcs whatsnew --unified > ../got+diff ../exp ../got
@@ -0,0 +1,11 @@+. lib++darcs init R+cd R+touch f+darcs record -l f -am'add f'+darcs rebase suspend -a --last 1+echo bla > g+darcs add g+darcs revert -a+not grep 'DO NOT TOUCH' _darcs/patches/unrevert
@@ -0,0 +1,16 @@+. lib++darcs init R+cd R+touch f+darcs record -l f -am "add f in R"+cd ..++darcs init S+cd S+touch f+darcs record -l f -am "add f in S"++# first y is for the "repos are unrelated" prompt+echo yaa | darcs rebase pull ../R --reorder-patches+darcs rebase log
@@ -5,10 +5,12 @@ . lib -rm -rf temp1 temp_a temp_b temp_c temp_d+# this test fails for darcs-1 repos+skip-formats darcs-1+ mkdir temp1 cd temp1-darcs init --darcs-2+darcs init echo 0 > f darcs add f darcs record -am 00@@ -38,5 +40,3 @@ not grep -i "no remote" log not grep -i get_extra log cd ..--rm -rf temp1 temp_a temp_b temp_c temp_d log
@@ -1,14 +1,13 @@ #!/usr/bin/env bash -#pragma repo-format darcs-2- . ./lib -rm -rf temp1 temp2+# this test fails in the darcs 1 format+skip-formats darcs-1+ mkdir temp1 cd temp1-# this test fails in the darcs 1 format-darcs init --darcs-2+darcs init echo A > f darcs add f darcs record --ignore-times -a -m A@@ -29,5 +28,3 @@ (darcs push -a || :) 2> push-result grep "Refusing to apply" push-result cd ..--rm -rf temp1 temp2
@@ -8,7 +8,7 @@ darcs init --no-patch-index # this test only applies to hashed formats-if cat _darcs/inventory; then exit 0; fi+if cat _darcs/inventory; then exit 200; fi date > f1 darcs add f1
@@ -21,12 +21,11 @@ ## 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+# this test fails for darcs-1 repos+skip-formats darcs-1+ darcs init --repo R darcs init --repo S
@@ -58,13 +58,12 @@ grep 'num 1' out cd ..-rm -rf temp1 # Some tests for the output of log when combined with move. -darcs init temp1-cd temp1+darcs init temp2+cd temp2 date > foo darcs record -lam 'add foo' mkdir d@@ -91,27 +90,25 @@ 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+darcs init temp3+cd temp3 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+darcs init temp4+cd temp4 mkdir d darcs record -lam d d # We use --match 'touch d/f' instead of simply d/f because the latter@@ -119,13 +116,12 @@ 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+darcs init temp5+cd temp5 mkdir d darcs record -lam 'added directory d'@@ -136,13 +132,12 @@ 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+darcs init temp6+cd temp6 echo a > a ; darcs rec -lam "patch_a" darcs log --context | grep patch_a@@ -156,6 +151,5 @@ darcs log --context | grep tag_a darcs log --context | grep patch_b cd ..-rm -rf temp1
@@ -0,0 +1,70 @@+#!/usr/bin/env bash++. ./lib++# detect move of a file and then a replace in that file++test_setup() {+ num=$1+ shift+ paths=$@++ darcs init R$num+ cd R$num++ echo foo > old+ echo foo >> old+ echo bar >> old++ darcs record -lam 'added old'+ mv old new++ echo bar > new + echo bar >> new+ echo bar >> new++ darcs whatsnew --look-for-moves --look-for-replaces $paths > ../out.actual+ cd ..+}++cat <<EOF > out.expected+move ./old ./new+hunk ./new 3+-bar++foo+replace ./new [A-Za-z_0-9] foo bar+EOF++test_setup 1 ""+diff out.actual out.expected++# same but only for old++test_setup 2 old+# remove the line about What's new in: old+sed -i '1d' out.actual+diff out.actual out.expected++# same but only for new++test_setup 3 new+# remove the line about What's new in: new+sed -i '1d' out.actual+# NOTE: the move is NOT detected in this case+# We might want to change that...+sed '1d' out.expected | sed 's/new/old/' > out.expected3+diff out.actual out.expected3++# same but only for old and new++test_setup 4 old new+# remove the line about What's new in: old new+sed -i '1d' out.actual+diff out.actual out.expected++# same but only for old and new and non-existing++test_setup 5 old new non-existing+# remove the line about What's new in: old new, and the one that reports non-existing+sed -i '1,2d' out.actual+diff out.actual out.expected
@@ -0,0 +1,50 @@+#!/usr/bin/env bash++# test that --look-for-moves properly handles file arguments++. ./lib++darcs init R+cd R++touch old1+touch old2+touch old3+darcs record -lam 'added files'+mv old1 new1+mv old2 new2+cd ..++runtest () {+ darcs whatsnew --repodir R --look-for-moves $* | grep move > out+}++num_lines () {+ test "$(cat $2 | wc -l)" = "$1"+}++move1='move \./old1 \./new1'+move2='move \./old2 \./new2'++runtest ""+grep "$move1" out+grep "$move2" out+num_lines 2 out++runtest old1+# we expect to see only move1+grep "$move1" out+not grep "$move2" out+num_lines 1 out++runtest old1 old2+# we expect to see both moves+grep "$move1" out+grep "$move2" out+num_lines 2 out++runtest old2 old3+# we expect to see only move2+grep "$move2" out+not grep "$move1" out+num_lines 1 out
@@ -5,35 +5,23 @@ # A test for darcs detecting a conflict, inspired by bug #152 in RT -rm -rf temp1 temp2- # set up the repository-mkdir temp1-cd temp1-darcs init-cd ..+darcs init temp1 cd temp1 echo "apply allow-conflicts" > _darcs/prefs/defaults-# note: to make this pass, change echo to echo -n-# is that right? echo "from temp1" > one.txt darcs add one.txt-darcs record -A bar -am "add one.txt"+darcs record -am "add one.txt" echo >> one.txt darcs wh -u cd .. darcs get temp1 temp2 cd temp2-# reality check-darcs show files | grep one.txt-echo "in tmp2" >> one.txt+echo "from temp2" >> one.txt darcs whatsnew -s | grep M-darcs record -A bar -am "add extra line"+darcs record -am "append non-empty line" darcs push -av > log-cat log-not grep -i conflicts log+grep -i conflicts log cd ..--rm -rf temp1 temp2
@@ -4,21 +4,13 @@ # 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+skip-formats darcs-1 # compressed repo is darcs-2 -gunzip -c $TESTDATA/many-files--${format}.tgz | tar xf -+gunzip -c $TESTDATA/laziness-complete.tgz | tar xf - -cd many*+cd repo darcs optimize http test -e _darcs/packs/basic.tar.gz@@ -28,13 +20,13 @@ 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"+darcs clone $baseurl/laziness-complete 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"+darcs clone $baseurl/laziness-complete 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"+rm -rf laziness-complete/_darcs/packs/+darcs clone $baseurl/laziness-complete S --verbose |not grep "Cloning packed basic repository"
@@ -1,21 +1,13 @@ #!/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+skip-formats darcs-1 # compressed repo is darcs-2 -gunzip -c $TESTDATA/many-files--${format}.tgz | tar xf -+gunzip -c $TESTDATA/laziness-complete.tgz | tar xf - -cd many*+cd repo darcs optimize http test -e _darcs/packs/basic.tar.gz@@ -23,7 +15,7 @@ cd .. serve_http # sets baseurl-darcs clone --packs $baseurl/many-files--${format} S+darcs clone --packs $baseurl/laziness-complete S cd S rm _darcs/prefs/sources # avoid any further contact with the original repository darcs check
@@ -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 ]
@@ -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 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 ]
@@ -36,7 +36,7 @@ [ -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+( http_proxy= 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
@@ -47,7 +47,6 @@ cd .. darcs clone $REMOTE:$REMOTE_DIR/R S --debug > log 2>&1-COUNT=$(grep -c 'darcs transfer-mode' log)+COUNT=$(grep -c '^Exec.*darcs.*transfer-mode' log) # with issue2090, this was 6! test $COUNT -eq 1-cleanup
@@ -0,0 +1,45 @@+#!/usr/bin/env bash+## Test for issue2545 - Argument smuggling in SSH repository URLs+##+## Darcs allows (almost) arbitrary command execution via a crafted ssh+## URI.+## When pushing to a remote repo, darcs is invoked on the remote server+## via ssh. This use of ssh is different from the ones tested by the+## not-networked test. Also, I'm not sure how (if) it can be exploited,+## so I'm just checking for the debug message. Pretty lame test, I know.+##+## Copyright (C) 2017 Gian Piero Carrubba+##+## 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.+. sshlib # Load ssh helpers.++init_remote_repo R # Create our test repos.+darcs init --repo R #+cd R++echo "text" > file # Modify the working dir+darcs record -lam "First Patch" # Record the changes++check="\"${SSH}\" \"--\" \"${REMOTE}\" \"darcs apply --all --debug --repodir '${REMOTE_DIR}/R'\""+darcs push -a --debug "${REMOTE}":"${REMOTE_DIR}"/R 2>&1 >/dev/null | \+ fgrep "$check"
@@ -103,4 +103,3 @@ grep -q 'conflicts options to apply' log cd ..-cleanup
@@ -1,5 +1,5 @@ if [ x${REMOTE_DIR} = x ]; then- REMOTE_DIR=/tmp/darcs-ssh-test+ REMOTE_DIR=/tmp/darcs-ssh-test$$ fi if [ x"${USE_PUTTY}" != x ]; then@@ -22,20 +22,19 @@ SSH=${DARCS_SSH} fi -rm -rf tempssh-mkdir tempssh-cd tempssh--cleanup () {- cd ..- rm -rf tempssh-}- if [ x${REMOTE} = x ]; then REMOTE=$(whoami)@localhost fi -# vim: syntax=sh:+init_remote_repo() {+ repodir="${1:-R}" + ${SSH} ${REMOTE} \+ "rm -rf '${REMOTE_DIR}' && mkdir '${REMOTE_DIR}' && \+ cd '${REMOTE_DIR}' && darcs init --repo '$repodir' --$format"+}+ # test if we can connect via ssh, otherwise skip test-${SSH} ${REMOTE} true || exit 200+${SSH} -x -o=NumberofPasswordPrompts=0 ${REMOTE} true || exit 200++# vim: syntax=sh:
@@ -76,7 +76,7 @@ cd .. -# issue2253 - darcs log FILE should build patch index in an OF repo+# issue2253 - darcs log FILE should not build patch index in an OF repo cd old darcs log x
@@ -4,7 +4,6 @@ . lib -rm -rf temp1 mkdir temp1 cd temp1 darcs init@@ -15,7 +14,7 @@ env # clear all output formating environment variables for e in DARCS_DONT_ESCAPE_ISPRINT DARCS_USE_ISPRINT\- DARCS_DONT_ESCAPE_8BIT\+ DARCS_ESCAPE_8BIT\ DARCS_DONT_ESCAPE_EXTRA DARCS_ESCAPE_EXTRA\ DARCS_DONT_ESCAPE_TRAILING_SPACES\ DARCS_DONT_COLOR DARCS_ALWAYS_COLOR DARCS_ALTERNATIVE_COLOR\@@ -30,7 +29,7 @@ test_line () { rm -f a echo $1 > a- darcs whatsnew | fgrep $2+ darcs whatsnew | grep -F $2 } @@ -62,6 +61,11 @@ test_line $(printf '\x1D') '[_^]_]' test_line $(printf '\x1E') '[_^^_]' test_line $(printf '\x1F') '[_^__]'++# everything up to here is escaped by default;+# for the rest we must explicitly set it:+export DARCS_ESCAPE_8BIT=1+ test_line $(printf '\x7F') '[_^?_]' # other chars are escaped with <U+XXXX> test_line $(printf '\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F')\@@ -80,4 +84,3 @@ '[_<U+00E0>_][_<U+00E1>_][_<U+00E2>_][_<U+00E3>_][_<U+00E4>_][_<U+00E5>_][_<U+00E6>_][_<U+00E7>_][_<U+00E8>_][_<U+00E9>_][_<U+00EA>_][_<U+00EB>_][_<U+00EC>_][_<U+00ED>_][_<U+00EE>_][_<U+00EF>_]' test_line $(printf '\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF')\ '[_<U+00F0>_][_<U+00F1>_][_<U+00F2>_][_<U+00F3>_][_<U+00F4>_][_<U+00F5>_][_<U+00F6>_][_<U+00F7>_][_<U+00F8>_][_<U+00F9>_][_<U+00FA>_][_<U+00FB>_][_<U+00FC>_][_<U+00FD>_][_<U+00FE>_][_<U+00FF>_]'-rm -rf temp1
@@ -320,3 +320,22 @@ rm -rf temp1 temp2 +# pull --matches+darcs init temp1+cd temp1+echo first > a+darcs record -lam 'first'+firsthash=`darcs log --xml | grep 'hash=' | sed -e "s/.*hash='//" -e "s/'>//"`+echo second > b+darcs record -lam 'second'+cd ..++darcs init temp2+darcs pull --repodir temp2 -a --match "hash $firsthash" temp1+test $(darcs log --count --repodir temp2) -eq 1++darcs init temp3+darcs pull --repodir temp3 -a --hash $firsthash temp1+test $(darcs log --count --repodir temp3) -eq 1++rm -rf temp1 temp2 temp3
@@ -1,29 +0,0 @@-#!/usr/bin/env bash-. lib--#pragma repo-format darcs-1,darcs-2--rm -rf temp1 temp2--if grep darcs-1 .darcs/defaults; then-format=hashed-elif grep darcs-2 .darcs/defaults; then-format=darcs-2-else format=ERROR; fi--mkdir temp2-cd temp2-gunzip -c $TESTDATA/many-files--${format}.tgz | tar xf --cd ..--mkdir temp1-cd temp1-darcs init-darcs pull -a ../temp2/many-files--${format} > log-grep -i 'finished pulling' log-cd ..-rm -rf temp1--# put things back how we found them.--rm -rf temp1 temp2
@@ -27,7 +27,7 @@ # push without a repo gives an error cd temp1 not darcs push -p 123 2> log-grep -i 'missing argument' log+grep -i 'No default repository to push to' log cd .. mkdir -p temp2/one/two
@@ -53,7 +53,7 @@ # (should it answer 'yes' to both pulling and suspending? echo yyy | darcs rebase apply -a ../R2/2.dpatch -darcs changes --count | grep "Rebase in progress: 2 suspended patches"+darcs changes --count 2>&1 | grep "Rebase in progress: 2 suspended patches" echo yny | darcs rebase unsuspend | grep "We have conflicts" cat > expected <<EOF v v v v v v v
@@ -33,7 +33,7 @@ echo 'wibble' > wibble darcs rec -lam "add wibble" -echo 'yy' | darcs rebase suspend | grep-once "Rebase in progress: 1 suspended patch"+echo 'yy' | darcs rebase suspend 2>&1 | grep-once "Rebase in progress: 1 suspended patch" not test -f wibble echo 'yy' | darcs rebase unsuspend | grep-once "Rebase finished" test -f wibble
@@ -54,10 +54,9 @@ * 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 - -echo 'yy' | darcs rebase unsuspend | grep "Rebase finished"+echo 'yy' | darcs rebase unsuspend 2>&1 | grep "Rebase finished" test -f wibble
@@ -52,7 +52,7 @@ # (should it answer 'yes' to both pulling and suspending? echo yyy | darcs rebase pull -a ../R2 -darcs changes --count | grep "Rebase in progress: 2 suspended patches"+darcs changes --count 2>&1 | grep "Rebase in progress: 2 suspended patches" echo yny | darcs rebase unsuspend | grep "We have conflicts" cat > expected <<EOF v v v v v v v
@@ -1,7 +1,5 @@ #!/usr/bin/env bash -#pragma repo-format darcs-1,darcs-2- . lib rm -rf garbage future@@ -13,7 +11,7 @@ mkdir future cd future-darcs init+darcs init touch titi darcs add titi darcs record -am titi@@ -68,9 +66,7 @@ # alternative of a line of format # only look at future vs darcs2 -if grep darcs-1 .darcs/defaults; then- exit 200-fi+skip-formats darcs-1 # get future repo: ok # --to-match is needed because of bug###
@@ -0,0 +1,87 @@+#!/usr/bin/env bash+. ./lib++rm -rf temp1+mkdir temp1+cd temp1+darcs init+echo hello world > foo+darcs add foo+darcs record -lam add+echo goodbye world >> foo+darcs revert -a+darcs show contents foo | cmp foo -++# Now let's test a trickier revert where changes commute nontrivially.++cat > foo <<EOF+a+b+c+d+e+EOF++darcs record -am cleanup++mv foo foo.tmp+cat foo.tmp | grep -v b | grep -v d > foo++echo "nyy" | darcs revert++DARCS_DONT_COLOR=1 darcs wh > whatsnew+cat > correct <<EOF+hunk ./foo 2+-b+EOF+diff -c correct whatsnew++# Try a situation where earlier (kept) changes are depended upon by the+# changes we want to revert:++darcs record -am cleanup++echo hello world > bar+echo hello world > foo+darcs add bar+darcs replace hello goodbye bar foo++echo "cnnnyy/y" | tr / \\012 | darcs revert++DARCS_DONT_COLOR=1 darcs wh > whatsnew+cat > correct <<EOF+addfile ./bar+hunk ./bar 1++goodbye world+hunk ./foo 1+-a+-c+-d+-e++hello world+EOF+diff -c correct whatsnew++cd ..+rm -rf temp1++# test for reverting removed directory++darcs init temp1+cd temp1+mkdir d+darcs add d+darcs record -lam 'add dir'+rmdir d+darcs revert -a d+cd ..+rm -rf temp1++# revert unrecorded add++darcs init temp1+cd temp1+echo stuff > foo+darcs add foo+darcs revert -a+
@@ -1,67 +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 add -A x-echo goodbye world >> foo-echo y/y | tr / \\012 | darcs revert-darcs show contents foo | cmp foo ---# Now let's test a trickier revert where changes commute nontrivially.--cat > foo <<EOF-a-b-c-d-e-EOF--darcs record -a -A me -m cleanup--mv foo foo.tmp-cat foo.tmp | grep -v b | grep -v d > foo--echo "nyy" | darcs revert--DARCS_DONT_COLOR=1 darcs wh > whatsnew-cat > correct <<EOF-hunk ./foo 2--b-EOF-diff -c correct whatsnew--# Try a situation where earlier (kept) changes are depended upon by the-# changes we want to revert:--darcs record -a -A me -m cleanup--echo hello world > bar-echo hello world > foo-darcs add bar-darcs replace hello goodbye bar foo--echo "cnnnyy/y" | tr / \\012 | darcs revert--DARCS_DONT_COLOR=1 darcs wh > whatsnew-cat > correct <<EOF-addfile ./bar-hunk ./bar 1-+goodbye world-hunk ./foo 1--a--c--d--e-+hello world-EOF-diff -c correct whatsnew--cd ..-rm -rf temp1-
@@ -1,13 +0,0 @@-#!/usr/bin/env bash-. ./lib--rm -rf temp-mkdir temp-cd temp--darcs init--echo stuff > foo-darcs add foo--darcs revert -a
@@ -24,11 +24,9 @@ ## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## SOFTWARE. -#pragma repo-format darcs-1- . lib # Load some portability helpers. -grep darcs-1 $HOME/.darcs/defaults || exit 200+skip-formats darcs-2 rm -rf empty mkdir empty
@@ -24,11 +24,9 @@ ## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## SOFTWARE. -#pragma repo-format darcs-2- . lib # Load some portability helpers. -grep darcs-2 $HOME/.darcs/defaults || exit 200+skip-formats darcs-1 rm -rf empty mkdir empty
@@ -1,25 +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 add -A x-echo goodbye world >> foo-darcs diff-darcs diff -u-darcs whatsnew-darcs whatsnew --summary-echo y | darcs revert -a-darcs show contents foo | cmp foo --mkdir d-darcs add d-darcs record -a -m 'add dir' -A x-rmdir d-darcs revert -a d-cd ..-rm -rf temp1-
@@ -1,22 +0,0 @@-#!/usr/bin/env bash--## All these commands SHOULD fail (hence leading NOTs).-. lib--darcs show bug --debug 1> stdout 2> stderr || true--cat stdout-cat stderr--echo The following test will fail if this version of darcs is marked as-echo obsolete.-echo ==================================================================--not grep 'please do not' stderr--# The following test fails if HTTP isn't present, but would be a nice test-# to have in place.--#not grep unable stderr--grep 'fake bug' stderr
@@ -89,3 +89,28 @@ cd .. rm -rf temp++darcs init --repo R+cd R++for file in foo bar baz quux+do+ touch "$file"+ darcs add "$file"+ darcs record --all --name "Add $file"+done++darcs unrecord --all --patches "quux"+darcs rebase suspend --all --patches "foo"++# (pending, match): (False, False)+check_manifest "bar baz" "" "--no-pending"+# (pending, match): (False, True)+check_manifest "bar" "" "--no-pending --patch bar"+# (pending, match): (True, False)+check_manifest "bar baz quux" "" "--pending"+# (pending, match): (True, True)+not darcs show files --pending --patch "bar"++cd ..+rm -rf R
@@ -24,8 +24,6 @@ ## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## SOFTWARE. -#pragma repo-format darcs-1,darcs-2- . lib # Load some portability helpers. if grep darcs-2 .darcs/defaults; then
@@ -0,0 +1,87 @@+. lib++switch_to_utf8_locale+lc_utf8=$LC_ALL++darcs init E+darcs clone E U++cd U+mkdir Texte+echo 'Müßiggang' > 'Texte/böse Zeichen'+darcs record -lam 'Erste Änderung'+darcs log -v+darcs send -ao bundle+tail -n+7 bundle > u_bundle++LC_ALL=C++darcs log -v+darcs send -ao bundle+tail -n+7 bundle > c_bundle++diff u_bundle c_bundle++darcs apply u_bundle | grep 'already .*applied'+darcs obliterate -a+darcs apply u_bundle | grep 'Finished applying'+darcs unrecord -a+darcs revert -a+darcs apply u_bundle | grep 'Finished applying'+++LC_ALL=$lc_utf8++darcs apply c_bundle | grep 'already .*applied'+darcs obliterate -a+darcs apply c_bundle | grep 'Finished applying'+darcs unrecord -a+darcs revert -a+darcs apply c_bundle | grep 'Finished applying'++LC_ALL=C++cd ..++darcs clone U C+diff -r U/Texte C/Texte++cd C+darcs pull ../E --set-default++darcs apply ../U/u_bundle | grep 'already .*applied'+diff -r ../U/Texte Texte+darcs obliterate -ao ou_bundle+diff ../U/u_bundle ou_bundle+darcs apply ou_bundle | grep 'Finished applying'+diff -r ../U/Texte Texte+darcs unrecord -a+darcs revert -a+darcs pull ../U -a+diff -r ../U/Texte Texte+darcs send -ao bundle+tail -n+7 bundle > c_bundle++LC_ALL=$lc_utf8++darcs send -ao bundle+tail -n+7 bundle > u_bundle+diff u_bundle c_bundle++darcs apply c_bundle | grep 'already .*applied'+diff -r ../U/Texte Texte+darcs obliterate -ao oc_bundle+diff c_bundle oc_bundle+darcs apply c_bundle | grep 'Finished applying'+diff -r ../U/Texte Texte+darcs unrecord -a+darcs revert -a+darcs pull ../U -a+diff -r ../U/Texte Texte+darcs send -ao bundle+tail -n+7 bundle > c_bundle++cd ..++diff U/u_bundle C/u_bundle+diff -r U/Texte C/Texte
@@ -1,16 +0,0 @@-#!/usr/bin/env bash--. lib--DIR="`pwd`"--# set up the repository-rm -rf temp1 # another script may have left a mess.-mkdir temp1-cd temp1-darcs init-touch foo-darcs add foo-echo ny | darcs record-cd ..-rm -rf temp1
@@ -30,7 +30,8 @@ switch_to_utf8_locale -export DARCS_DONT_ESCAPE_8BIT=1+#is now the default, must use DARCS_ESCAPE_8BIT to disable+#export DARCS_DONT_ESCAPE_8BIT=1 rm -rf R mkdir R
@@ -24,11 +24,9 @@ ## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ## SOFTWARE. -#pragma repo-format darcs-1- . lib # Load some portability helpers. -grep darcs-1 $HOME/.darcs/defaults || exit 200+skip-formats darcs-2 rm -rf braced gunzip -c $TESTDATA/braced.tgz | tar xf -
@@ -0,0 +1,51 @@+#!/bin/sh -e+##+## Test for the interaction of --look-for-adds and --no-summary in darcs whatsnew+##+## Copyright (C) 2017 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++rm -rf R+mkdir R+cd R++darcs init++echo A > A+echo B > B++darcs add A+darcs rec -am "add A"++echo "A 2" > A++darcs whatsnew --look-for-adds --no-summary > output.txt++grep "hunk \./A 1" output.txt+grep "\-A" output.txt+grep "+A 2" output.txt+grep "addfile \./B" output.txt+grep "hunk \./B 1" output.txt+grep "+B" output.txt+
@@ -21,7 +21,8 @@ touch look_summary.txt darcs whatsnew -l | grep -i "a ./look_summary.txt" -#whatsnew works with uncommon file names+# whatsnew works with uncommon file names and does NOT display+# the internal "white space encoded" filename if echo $OS | grep -i windows; then echo test does not work on windows exit 0;@@ -29,13 +30,13 @@ echo foo > \\ darcs add \\ darcs whatsnew | tee log- grep 'hunk ./\\92\\' log+ grep 'hunk ./\\' log fi echo foo > "foo bar" darcs add "foo bar" darcs wh | tee log-grep 'hunk ./foo\\32\\bar' log+grep 'hunk ./foo bar' log # check that filename encoding does not botch up the index darcs rec -am "weird filenames"@@ -141,10 +142,9 @@ 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+darcs wh foo foo/../foo/. > out+cat out+grep date out | wc -l | grep 1 cd .. rm -rf temp1