git-repair 1.20170626 → 1.20200102
raw patch · 68 files changed
+2246/−1696 lines, 68 filesdep +attoparsecdep +deepseqdep +filepath-bytestringdep ~basedep ~networkdep ~optparse-applicative
Dependencies added: attoparsec, deepseq, filepath-bytestring
Dependency ranges changed: base, network, optparse-applicative, unix, unix-compat
Files
- Build/Configure.hs +4/−4
- Build/TestConfig.hs +2/−15
- BuildInfo.hs +12/−0
- CHANGELOG +10/−0
- COPYRIGHT +695/−0
- Common.hs +2/−1
- GPL +0/−674
- Git.hs +21/−20
- Git/Branch.hs +17/−15
- Git/BuildVersion.hs +3/−3
- Git/CatFile.hs +89/−18
- Git/Command.hs +35/−20
- Git/Config.hs +70/−45
- Git/Construct.hs +22/−18
- Git/CurrentRepo.hs +35/−9
- Git/Destroyer.hs +2/−2
- Git/DiffTreeItem.hs +1/−1
- Git/FilePath.hs +33/−22
- Git/Filename.hs +37/−16
- Git/Fsck.hs +30/−29
- Git/HashObject.hs +76/−0
- Git/Index.hs +11/−21
- Git/LsFiles.hs +84/−62
- Git/LsTree.hs +55/−30
- Git/Objects.hs +2/−2
- Git/Ref.hs +66/−25
- Git/RefLog.hs +2/−2
- Git/Remote.hs +23/−10
- Git/Repair.hs +19/−19
- Git/Sha.hs +1/−1
- Git/Types.hs +74/−37
- Git/UpdateIndex.hs +50/−19
- Git/Url.hs +8/−2
- Git/Version.hs +1/−1
- Utility/Applicative.hs +5/−1
- Utility/Attoparsec.hs +21/−0
- Utility/Batch.hs +10/−7
- Utility/Data.hs +4/−1
- Utility/Directory.hs +2/−93
- Utility/DottedVersion.hs +9/−2
- Utility/Env.hs +8/−25
- Utility/Env/Basic.hs +25/−0
- Utility/Env/Set.hs +43/−0
- Utility/Exception.hs +1/−17
- Utility/FileMode.hs +3/−2
- Utility/FileSize.hs +10/−2
- Utility/FileSystemEncoding.hs +79/−14
- Utility/Format.hs +5/−5
- Utility/HumanNumber.hs +1/−1
- Utility/HumanTime.hs +7/−5
- Utility/Metered.hs +89/−46
- Utility/Misc.hs +20/−22
- Utility/Monad.hs +13/−1
- Utility/PartialPrelude.hs +15/−6
- Utility/Path.hs +41/−60
- Utility/PosixFiles.hs +0/−42
- Utility/Process.hs +11/−80
- Utility/QuickCheck.hs +15/−17
- Utility/Rsync.hs +49/−2
- Utility/SafeCommand.hs +28/−10
- Utility/Split.hs +10/−1
- Utility/ThreadScheduler.hs +8/−5
- Utility/Tmp.hs +11/−54
- Utility/Tmp/Dir.hs +70/−0
- Utility/Tuple.hs +5/−1
- Utility/UserInfo.hs +14/−8
- git-repair.cabal +20/−20
- git-repair.hs +2/−3
Build/Configure.hs view
@@ -1,12 +1,13 @@-{- Checks system configuration and generates SysConfig.hs. -}+{- Checks system configuration and generates SysConfig. -} {-# OPTIONS_GHC -fno-warn-tabs #-} module Build.Configure where import System.Environment-import Control.Applicative import Control.Monad.IfElse+import Control.Applicative+import Prelude import Build.TestConfig import Build.Version@@ -15,7 +16,7 @@ tests :: [TestCase] tests = [ TestCase "version" (Config "packageversion" . StringConfig <$> getVersion)- , TestCase "git" $ requireCmd "git" "git --version >/dev/null"+ , TestCase "git" $ testCmd "git" "git --version >/dev/null" , TestCase "git version" getGitVersion ] @@ -25,7 +26,6 @@ run :: [TestCase] -> IO () run ts = do- args <- getArgs config <- runTests ts writeSysConfig config whenM (isReleaseBuild) $
Build/TestConfig.hs view
@@ -1,4 +1,4 @@-{- Tests the system and generates Build.SysConfig.hs. -}+{- Tests the system and generates SysConfig. -} {-# OPTIONS_GHC -fno-warn-tabs #-} @@ -42,12 +42,11 @@ valuetype (MaybeBoolConfig _) = "Maybe Bool" writeSysConfig :: [Config] -> IO ()-writeSysConfig config = writeFile "Build/SysConfig.hs" body+writeSysConfig config = writeFile "Build/SysConfig" body where body = unlines $ header ++ map show config ++ footer header = [ "{- Automatically generated. -}"- , "module Build.SysConfig where" , "" ] footer = []@@ -60,18 +59,6 @@ testEnd c rest <- runTests ts return $ c:rest--{- Tests that a command is available, aborting if not. -}-requireCmd :: ConfigKey -> String -> Test-requireCmd k cmdline = do- ret <- testCmd k cmdline- handle ret- where- handle r@(Config _ (BoolConfig True)) = return r- handle r = do- testEnd r- error $ "** the " ++ c ++ " command is required"- c = head $ words cmdline {- Checks if a command is available by running a command line. -} testCmd :: ConfigKey -> String -> Test
+ BuildInfo.hs view
@@ -0,0 +1,12 @@+{- build info+ -+ - Copyright 2017 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU AGPL version 3 or higher.+ -}++{-# LANGUAGE CPP #-}++module BuildInfo where++#include "Build/SysConfig"
CHANGELOG view
@@ -1,3 +1,13 @@+git-repair (1.20200102) unstable; urgency=medium++ * Relicensed AGPL.+ * Merge from git-annex.+ * Removed the network-uri build flag.+ * Increased required version of several dependencies.+ * Added dependencies on deepseq, attoparsec and filepath-bytestring.++ -- Joey Hess <id@joeyh.name> Thu, 02 Jan 2020 12:39:13 -0400+ git-repair (1.20170626) unstable; urgency=medium * Merge from git-annex.
+ COPYRIGHT view
@@ -0,0 +1,695 @@+Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/+Source: native package++Files: *+Copyright: © 2013-2019 Joey Hess <joey@kitenet.net>+License: AGPL-3+++Files: Utility/*+Copyright: 2012-2019 Joey Hess <joey@kitenet.net>+License: BSD-2-clause++License: BSD-2-clause+ Redistribution and use in source and binary forms, with or without+ modification, are permitted provided that the following conditions+ are met:+ 1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+ 2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+ .+ THIS SOFTWARE IS PROVIDED BY AUTHORS 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 AUTHORS OR CONTRIBUTORS BE LIABLE+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+ SUCH DAMAGE.++License: AGPL-3++ GNU AFFERO GENERAL PUBLIC LICENSE+ Version 3, 19 November 2007+ .+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>+ Everyone is permitted to copy and distribute verbatim copies+ of this license document, but changing it is not allowed.+ .+ Preamble+ .+ The GNU Affero General Public License is a free, copyleft license for+ software and other kinds of works, specifically designed to ensure+ cooperation with the community in the case of network server software.+ .+ The licenses for most software and other practical works are designed+ to take away your freedom to share and change the works. By contrast,+ our General Public Licenses are intended to guarantee your freedom to+ share and change all versions of a program--to make sure it remains free+ software for all its users.+ .+ When we speak of free software, we are referring to freedom, not+ price. Our General Public Licenses are designed to make sure that you+ have the freedom to distribute copies of free software (and charge for+ them if you wish), that you receive source code or can get it if you+ want it, that you can change the software or use pieces of it in new+ free programs, and that you know you can do these things.+ .+ Developers that use our General Public Licenses protect your rights+ with two steps: (1) assert copyright on the software, and (2) offer+ you this License which gives you legal permission to copy, distribute+ and/or modify the software.+ .+ A secondary benefit of defending all users' freedom is that+ improvements made in alternate versions of the program, if they+ receive widespread use, become available for other developers to+ incorporate. Many developers of free software are heartened and+ encouraged by the resulting cooperation. However, in the case of+ software used on network servers, this result may fail to come about.+ The GNU General Public License permits making a modified version and+ letting the public access it on a server without ever releasing its+ source code to the public.+ .+ The GNU Affero General Public License is designed specifically to+ ensure that, in such cases, the modified source code becomes available+ to the community. It requires the operator of a network server to+ provide the source code of the modified version running there to the+ users of that server. Therefore, public use of a modified version, on+ a publicly accessible server, gives the public access to the source+ code of the modified version.+ .+ An older license, called the Affero General Public License and+ published by Affero, was designed to accomplish similar goals. This is+ a different license, not a version of the Affero GPL, but Affero has+ released a new version of the Affero GPL which permits relicensing under+ this license.+ .+ The precise terms and conditions for copying, distribution and+ modification follow.+ .+ TERMS AND CONDITIONS+ .+ 0. Definitions.+ .+ "This License" refers to version 3 of the GNU Affero General Public License.+ .+ "Copyright" also means copyright-like laws that apply to other kinds of+ works, such as semiconductor masks.+ .+ "The Program" refers to any copyrightable work licensed under this+ License. Each licensee is addressed as "you". "Licensees" and+ "recipients" may be individuals or organizations.+ .+ To "modify" a work means to copy from or adapt all or part of the work+ in a fashion requiring copyright permission, other than the making of an+ exact copy. The resulting work is called a "modified version" of the+ earlier work or a work "based on" the earlier work.+ .+ A "covered work" means either the unmodified Program or a work based+ on the Program.+ .+ To "propagate" a work means to do anything with it that, without+ permission, would make you directly or secondarily liable for+ infringement under applicable copyright law, except executing it on a+ computer or modifying a private copy. Propagation includes copying,+ distribution (with or without modification), making available to the+ public, and in some countries other activities as well.+ .+ To "convey" a work means any kind of propagation that enables other+ parties to make or receive copies. Mere interaction with a user through+ a computer network, with no transfer of a copy, is not conveying.+ .+ An interactive user interface displays "Appropriate Legal Notices"+ to the extent that it includes a convenient and prominently visible+ feature that (1) displays an appropriate copyright notice, and (2)+ tells the user that there is no warranty for the work (except to the+ extent that warranties are provided), that licensees may convey the+ work under this License, and how to view a copy of this License. If+ the interface presents a list of user commands or options, such as a+ menu, a prominent item in the list meets this criterion.+ .+ 1. Source Code.+ .+ The "source code" for a work means the preferred form of the work+ for making modifications to it. "Object code" means any non-source+ form of a work.+ .+ A "Standard Interface" means an interface that either is an official+ standard defined by a recognized standards body, or, in the case of+ interfaces specified for a particular programming language, one that+ is widely used among developers working in that language.+ .+ The "System Libraries" of an executable work include anything, other+ than the work as a whole, that (a) is included in the normal form of+ packaging a Major Component, but which is not part of that Major+ Component, and (b) serves only to enable use of the work with that+ Major Component, or to implement a Standard Interface for which an+ implementation is available to the public in source code form. A+ "Major Component", in this context, means a major essential component+ (kernel, window system, and so on) of the specific operating system+ (if any) on which the executable work runs, or a compiler used to+ produce the work, or an object code interpreter used to run it.+ .+ The "Corresponding Source" for a work in object code form means all+ the source code needed to generate, install, and (for an executable+ work) run the object code and to modify the work, including scripts to+ control those activities. However, it does not include the work's+ System Libraries, or general-purpose tools or generally available free+ programs which are used unmodified in performing those activities but+ which are not part of the work. For example, Corresponding Source+ includes interface definition files associated with source files for+ the work, and the source code for shared libraries and dynamically+ linked subprograms that the work is specifically designed to require,+ such as by intimate data communication or control flow between those+ subprograms and other parts of the work.+ .+ The Corresponding Source need not include anything that users+ can regenerate automatically from other parts of the Corresponding+ Source.+ .+ The Corresponding Source for a work in source code form is that+ same work.+ .+ 2. Basic Permissions.+ .+ All rights granted under this License are granted for the term of+ copyright on the Program, and are irrevocable provided the stated+ conditions are met. This License explicitly affirms your unlimited+ permission to run the unmodified Program. The output from running a+ covered work is covered by this License only if the output, given its+ content, constitutes a covered work. This License acknowledges your+ rights of fair use or other equivalent, as provided by copyright law.+ .+ You may make, run and propagate covered works that you do not+ convey, without conditions so long as your license otherwise remains+ in force. You may convey covered works to others for the sole purpose+ of having them make modifications exclusively for you, or provide you+ with facilities for running those works, provided that you comply with+ the terms of this License in conveying all material for which you do+ not control copyright. Those thus making or running the covered works+ for you must do so exclusively on your behalf, under your direction+ and control, on terms that prohibit them from making any copies of+ your copyrighted material outside their relationship with you.+ .+ Conveying under any other circumstances is permitted solely under+ the conditions stated below. Sublicensing is not allowed; section 10+ makes it unnecessary.+ .+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.+ .+ No covered work shall be deemed part of an effective technological+ measure under any applicable law fulfilling obligations under article+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or+ similar laws prohibiting or restricting circumvention of such+ measures.+ .+ When you convey a covered work, you waive any legal power to forbid+ circumvention of technological measures to the extent such circumvention+ is effected by exercising rights under this License with respect to+ the covered work, and you disclaim any intention to limit operation or+ modification of the work as a means of enforcing, against the work's+ users, your or third parties' legal rights to forbid circumvention of+ technological measures.+ .+ 4. Conveying Verbatim Copies.+ .+ You may convey verbatim copies of the Program's source code as you+ receive it, in any medium, provided that you conspicuously and+ appropriately publish on each copy an appropriate copyright notice;+ keep intact all notices stating that this License and any+ non-permissive terms added in accord with section 7 apply to the code;+ keep intact all notices of the absence of any warranty; and give all+ recipients a copy of this License along with the Program.+ .+ You may charge any price or no price for each copy that you convey,+ and you may offer support or warranty protection for a fee.+ .+ 5. Conveying Modified Source Versions.+ .+ You may convey a work based on the Program, or the modifications to+ produce it from the Program, in the form of source code under the+ terms of section 4, provided that you also meet all of these conditions:+ .+ a) The work must carry prominent notices stating that you modified+ it, and giving a relevant date.+ .+ b) The work must carry prominent notices stating that it is+ released under this License and any conditions added under section+ 7. This requirement modifies the requirement in section 4 to+ "keep intact all notices".+ .+ c) You must license the entire work, as a whole, under this+ License to anyone who comes into possession of a copy. This+ License will therefore apply, along with any applicable section 7+ additional terms, to the whole of the work, and all its parts,+ regardless of how they are packaged. This License gives no+ permission to license the work in any other way, but it does not+ invalidate such permission if you have separately received it.+ .+ d) If the work has interactive user interfaces, each must display+ Appropriate Legal Notices; however, if the Program has interactive+ interfaces that do not display Appropriate Legal Notices, your+ work need not make them do so.+ .+ A compilation of a covered work with other separate and independent+ works, which are not by their nature extensions of the covered work,+ and which are not combined with it such as to form a larger program,+ in or on a volume of a storage or distribution medium, is called an+ "aggregate" if the compilation and its resulting copyright are not+ used to limit the access or legal rights of the compilation's users+ beyond what the individual works permit. Inclusion of a covered work+ in an aggregate does not cause this License to apply to the other+ parts of the aggregate.+ .+ 6. Conveying Non-Source Forms.+ .+ You may convey a covered work in object code form under the terms+ of sections 4 and 5, provided that you also convey the+ machine-readable Corresponding Source under the terms of this License,+ in one of these ways:+ .+ a) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by the+ Corresponding Source fixed on a durable physical medium+ customarily used for software interchange.+ .+ b) Convey the object code in, or embodied in, a physical product+ (including a physical distribution medium), accompanied by a+ written offer, valid for at least three years and valid for as+ long as you offer spare parts or customer support for that product+ model, to give anyone who possesses the object code either (1) a+ copy of the Corresponding Source for all the software in the+ product that is covered by this License, on a durable physical+ medium customarily used for software interchange, for a price no+ more than your reasonable cost of physically performing this+ conveying of source, or (2) access to copy the+ Corresponding Source from a network server at no charge.+ .+ c) Convey individual copies of the object code with a copy of the+ written offer to provide the Corresponding Source. This+ alternative is allowed only occasionally and noncommercially, and+ only if you received the object code with such an offer, in accord+ with subsection 6b.+ .+ d) Convey the object code by offering access from a designated+ place (gratis or for a charge), and offer equivalent access to the+ Corresponding Source in the same way through the same place at no+ further charge. You need not require recipients to copy the+ Corresponding Source along with the object code. If the place to+ copy the object code is a network server, the Corresponding Source+ may be on a different server (operated by you or a third party)+ that supports equivalent copying facilities, provided you maintain+ clear directions next to the object code saying where to find the+ Corresponding Source. Regardless of what server hosts the+ Corresponding Source, you remain obligated to ensure that it is+ available for as long as needed to satisfy these requirements.+ .+ e) Convey the object code using peer-to-peer transmission, provided+ you inform other peers where the object code and Corresponding+ Source of the work are being offered to the general public at no+ charge under subsection 6d.+ .+ A separable portion of the object code, whose source code is excluded+ from the Corresponding Source as a System Library, need not be+ included in conveying the object code work.+ .+ A "User Product" is either (1) a "consumer product", which means any+ tangible personal property which is normally used for personal, family,+ or household purposes, or (2) anything designed or sold for incorporation+ into a dwelling. In determining whether a product is a consumer product,+ doubtful cases shall be resolved in favor of coverage. For a particular+ product received by a particular user, "normally used" refers to a+ typical or common use of that class of product, regardless of the status+ of the particular user or of the way in which the particular user+ actually uses, or expects or is expected to use, the product. A product+ is a consumer product regardless of whether the product has substantial+ commercial, industrial or non-consumer uses, unless such uses represent+ the only significant mode of use of the product.+ .+ "Installation Information" for a User Product means any methods,+ procedures, authorization keys, or other information required to install+ and execute modified versions of a covered work in that User Product from+ a modified version of its Corresponding Source. The information must+ suffice to ensure that the continued functioning of the modified object+ code is in no case prevented or interfered with solely because+ modification has been made.+ .+ If you convey an object code work under this section in, or with, or+ specifically for use in, a User Product, and the conveying occurs as+ part of a transaction in which the right of possession and use of the+ User Product is transferred to the recipient in perpetuity or for a+ fixed term (regardless of how the transaction is characterized), the+ Corresponding Source conveyed under this section must be accompanied+ by the Installation Information. But this requirement does not apply+ if neither you nor any third party retains the ability to install+ modified object code on the User Product (for example, the work has+ been installed in ROM).+ .+ The requirement to provide Installation Information does not include a+ requirement to continue to provide support service, warranty, or updates+ for a work that has been modified or installed by the recipient, or for+ the User Product in which it has been modified or installed. Access to a+ network may be denied when the modification itself materially and+ adversely affects the operation of the network or violates the rules and+ protocols for communication across the network.+ .+ Corresponding Source conveyed, and Installation Information provided,+ in accord with this section must be in a format that is publicly+ documented (and with an implementation available to the public in+ source code form), and must require no special password or key for+ unpacking, reading or copying.+ .+ 7. Additional Terms.+ .+ "Additional permissions" are terms that supplement the terms of this+ License by making exceptions from one or more of its conditions.+ Additional permissions that are applicable to the entire Program shall+ be treated as though they were included in this License, to the extent+ that they are valid under applicable law. If additional permissions+ apply only to part of the Program, that part may be used separately+ under those permissions, but the entire Program remains governed by+ this License without regard to the additional permissions.+ .+ When you convey a copy of a covered work, you may at your option+ remove any additional permissions from that copy, or from any part of+ it. (Additional permissions may be written to require their own+ removal in certain cases when you modify the work.) You may place+ additional permissions on material, added by you to a covered work,+ for which you have or can give appropriate copyright permission.+ .+ Notwithstanding any other provision of this License, for material you+ add to a covered work, you may (if authorized by the copyright holders of+ that material) supplement the terms of this License with terms:+ .+ a) Disclaiming warranty or limiting liability differently from the+ terms of sections 15 and 16 of this License; or+ .+ b) Requiring preservation of specified reasonable legal notices or+ author attributions in that material or in the Appropriate Legal+ Notices displayed by works containing it; or+ .+ c) Prohibiting misrepresentation of the origin of that material, or+ requiring that modified versions of such material be marked in+ reasonable ways as different from the original version; or+ .+ d) Limiting the use for publicity purposes of names of licensors or+ authors of the material; or+ .+ e) Declining to grant rights under trademark law for use of some+ trade names, trademarks, or service marks; or+ .+ f) Requiring indemnification of licensors and authors of that+ material by anyone who conveys the material (or modified versions of+ it) with contractual assumptions of liability to the recipient, for+ any liability that these contractual assumptions directly impose on+ those licensors and authors.+ .+ All other non-permissive additional terms are considered "further+ restrictions" within the meaning of section 10. If the Program as you+ received it, or any part of it, contains a notice stating that it is+ governed by this License along with a term that is a further+ restriction, you may remove that term. If a license document contains+ a further restriction but permits relicensing or conveying under this+ License, you may add to a covered work material governed by the terms+ of that license document, provided that the further restriction does+ not survive such relicensing or conveying.+ .+ If you add terms to a covered work in accord with this section, you+ must place, in the relevant source files, a statement of the+ additional terms that apply to those files, or a notice indicating+ where to find the applicable terms.+ .+ Additional terms, permissive or non-permissive, may be stated in the+ form of a separately written license, or stated as exceptions;+ the above requirements apply either way.+ .+ 8. Termination.+ .+ You may not propagate or modify a covered work except as expressly+ provided under this License. Any attempt otherwise to propagate or+ modify it is void, and will automatically terminate your rights under+ this License (including any patent licenses granted under the third+ paragraph of section 11).+ .+ However, if you cease all violation of this License, then your+ license from a particular copyright holder is reinstated (a)+ provisionally, unless and until the copyright holder explicitly and+ finally terminates your license, and (b) permanently, if the copyright+ holder fails to notify you of the violation by some reasonable means+ prior to 60 days after the cessation.+ .+ Moreover, your license from a particular copyright holder is+ reinstated permanently if the copyright holder notifies you of the+ violation by some reasonable means, this is the first time you have+ received notice of violation of this License (for any work) from that+ copyright holder, and you cure the violation prior to 30 days after+ your receipt of the notice.+ .+ Termination of your rights under this section does not terminate the+ licenses of parties who have received copies or rights from you under+ this License. If your rights have been terminated and not permanently+ reinstated, you do not qualify to receive new licenses for the same+ material under section 10.+ .+ 9. Acceptance Not Required for Having Copies.+ .+ You are not required to accept this License in order to receive or+ run a copy of the Program. Ancillary propagation of a covered work+ occurring solely as a consequence of using peer-to-peer transmission+ to receive a copy likewise does not require acceptance. However,+ nothing other than this License grants you permission to propagate or+ modify any covered work. These actions infringe copyright if you do+ not accept this License. Therefore, by modifying or propagating a+ covered work, you indicate your acceptance of this License to do so.+ .+ 10. Automatic Licensing of Downstream Recipients.+ .+ Each time you convey a covered work, the recipient automatically+ receives a license from the original licensors, to run, modify and+ propagate that work, subject to this License. You are not responsible+ for enforcing compliance by third parties with this License.+ .+ An "entity transaction" is a transaction transferring control of an+ organization, or substantially all assets of one, or subdividing an+ organization, or merging organizations. If propagation of a covered+ work results from an entity transaction, each party to that+ transaction who receives a copy of the work also receives whatever+ licenses to the work the party's predecessor in interest had or could+ give under the previous paragraph, plus a right to possession of the+ Corresponding Source of the work from the predecessor in interest, if+ the predecessor has it or can get it with reasonable efforts.+ .+ You may not impose any further restrictions on the exercise of the+ rights granted or affirmed under this License. For example, you may+ not impose a license fee, royalty, or other charge for exercise of+ rights granted under this License, and you may not initiate litigation+ (including a cross-claim or counterclaim in a lawsuit) alleging that+ any patent claim is infringed by making, using, selling, offering for+ sale, or importing the Program or any portion of it.+ .+ 11. Patents.+ .+ A "contributor" is a copyright holder who authorizes use under this+ License of the Program or a work on which the Program is based. The+ work thus licensed is called the contributor's "contributor version".+ .+ A contributor's "essential patent claims" are all patent claims+ owned or controlled by the contributor, whether already acquired or+ hereafter acquired, that would be infringed by some manner, permitted+ by this License, of making, using, or selling its contributor version,+ but do not include claims that would be infringed only as a+ consequence of further modification of the contributor version. For+ purposes of this definition, "control" includes the right to grant+ patent sublicenses in a manner consistent with the requirements of+ this License.+ .+ Each contributor grants you a non-exclusive, worldwide, royalty-free+ patent license under the contributor's essential patent claims, to+ make, use, sell, offer for sale, import and otherwise run, modify and+ propagate the contents of its contributor version.+ .+ In the following three paragraphs, a "patent license" is any express+ agreement or commitment, however denominated, not to enforce a patent+ (such as an express permission to practice a patent or covenant not to+ sue for patent infringement). To "grant" such a patent license to a+ party means to make such an agreement or commitment not to enforce a+ patent against the party.+ .+ If you convey a covered work, knowingly relying on a patent license,+ and the Corresponding Source of the work is not available for anyone+ to copy, free of charge and under the terms of this License, through a+ publicly available network server or other readily accessible means,+ then you must either (1) cause the Corresponding Source to be so+ available, or (2) arrange to deprive yourself of the benefit of the+ patent license for this particular work, or (3) arrange, in a manner+ consistent with the requirements of this License, to extend the patent+ license to downstream recipients. "Knowingly relying" means you have+ actual knowledge that, but for the patent license, your conveying the+ covered work in a country, or your recipient's use of the covered work+ in a country, would infringe one or more identifiable patents in that+ country that you have reason to believe are valid.+ .+ If, pursuant to or in connection with a single transaction or+ arrangement, you convey, or propagate by procuring conveyance of, a+ covered work, and grant a patent license to some of the parties+ receiving the covered work authorizing them to use, propagate, modify+ or convey a specific copy of the covered work, then the patent license+ you grant is automatically extended to all recipients of the covered+ work and works based on it.+ .+ A patent license is "discriminatory" if it does not include within+ the scope of its coverage, prohibits the exercise of, or is+ conditioned on the non-exercise of one or more of the rights that are+ specifically granted under this License. You may not convey a covered+ work if you are a party to an arrangement with a third party that is+ in the business of distributing software, under which you make payment+ to the third party based on the extent of your activity of conveying+ the work, and under which the third party grants, to any of the+ parties who would receive the covered work from you, a discriminatory+ patent license (a) in connection with copies of the covered work+ conveyed by you (or copies made from those copies), or (b) primarily+ for and in connection with specific products or compilations that+ contain the covered work, unless you entered into that arrangement,+ or that patent license was granted, prior to 28 March 2007.+ .+ Nothing in this License shall be construed as excluding or limiting+ any implied license or other defenses to infringement that may+ otherwise be available to you under applicable patent law.+ .+ 12. No Surrender of Others' Freedom.+ .+ If conditions are imposed on you (whether by court order, agreement or+ otherwise) that contradict the conditions of this License, they do not+ excuse you from the conditions of this License. If you cannot convey a+ covered work so as to satisfy simultaneously your obligations under this+ License and any other pertinent obligations, then as a consequence you may+ not convey it at all. For example, if you agree to terms that obligate you+ to collect a royalty for further conveying from those to whom you convey+ the Program, the only way you could satisfy both those terms and this+ License would be to refrain entirely from conveying the Program.+ .+ 13. Remote Network Interaction; Use with the GNU General Public License.+ .+ Notwithstanding any other provision of this License, if you modify the+ Program, your modified version must prominently offer all users+ interacting with it remotely through a computer network (if your version+ supports such interaction) an opportunity to receive the Corresponding+ Source of your version by providing access to the Corresponding Source+ from a network server at no charge, through some standard or customary+ means of facilitating copying of software. This Corresponding Source+ shall include the Corresponding Source for any work covered by version 3+ of the GNU General Public License that is incorporated pursuant to the+ following paragraph.+ .+ Notwithstanding any other provision of this License, you have+ permission to link or combine any covered work with a work licensed+ under version 3 of the GNU General Public License into a single+ combined work, and to convey the resulting work. The terms of this+ License will continue to apply to the part which is the covered work,+ but the work with which it is combined will remain governed by version+ 3 of the GNU General Public License.+ .+ 14. Revised Versions of this License.+ .+ The Free Software Foundation may publish revised and/or new versions of+ the GNU Affero General Public License from time to time. Such new versions+ will be similar in spirit to the present version, but may differ in detail to+ address new problems or concerns.+ .+ Each version is given a distinguishing version number. If the+ Program specifies that a certain numbered version of the GNU Affero General+ Public License "or any later version" applies to it, you have the+ option of following the terms and conditions either of that numbered+ version or of any later version published by the Free Software+ Foundation. If the Program does not specify a version number of the+ GNU Affero General Public License, you may choose any version ever published+ by the Free Software Foundation.+ .+ If the Program specifies that a proxy can decide which future+ versions of the GNU Affero General Public License can be used, that proxy's+ public statement of acceptance of a version permanently authorizes you+ to choose that version for the Program.+ .+ Later license versions may give you additional or different+ permissions. However, no additional obligations are imposed on any+ author or copyright holder as a result of your choosing to follow a+ later version.+ .+ 15. Disclaimer of Warranty.+ .+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.+ .+ 16. Limitation of Liability.+ .+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF+ SUCH DAMAGES.+ .+ 17. Interpretation of Sections 15 and 16.+ .+ If the disclaimer of warranty and limitation of liability provided+ above cannot be given local legal effect according to their terms,+ reviewing courts shall apply local law that most closely approximates+ an absolute waiver of all civil liability in connection with the+ Program, unless a warranty or assumption of liability accompanies a+ copy of the Program in return for a fee.+ .+ END OF TERMS AND CONDITIONS+ .+ How to Apply These Terms to Your New Programs+ .+ If you develop a new program, and you want it to be of the greatest+ possible use to the public, the best way to achieve this is to make it+ free software which everyone can redistribute and change under these terms.+ .+ To do so, attach the following notices to the program. It is safest+ to attach them to the start of each source file to most effectively+ state the exclusion of warranty; and each file should have at least+ the "copyright" line and a pointer to where the full notice is found.+ .+ <one line to give the program's name and a brief idea of what it does.>+ Copyright (C) <year> <name of author>+ .+ This program is free software: you can redistribute it and/or modify+ it under the terms of the GNU Affero General Public License as published by+ the Free Software Foundation, either version 3 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 Affero General Public License for more details.+ .+ You should have received a copy of the GNU Affero General Public License+ along with this program. If not, see <http://www.gnu.org/licenses/>.+ .+ Also add information on how to contact you by electronic and paper mail.+ .+ If your software can interact with users remotely through a computer+ network, you should also make sure that it provides a way for users to+ get its source. For example, if your program is a web application, its+ interface could display a "Source" link that leads users to an archive+ of the code. There are many ways you could offer source, and different+ solutions will be better for different programs; see section 13 for the+ specific requirements.+ .+ You should also get your employer (if you work as a programmer) or school,+ if any, to sign a "copyright disclaimer" for the program, if necessary.+ For more information on this, and how to apply and follow the GNU AGPL, see+ <http://www.gnu.org/licenses/>.
Common.hs view
@@ -18,6 +18,7 @@ import System.Posix.IO as X hiding (createPipe) #endif import System.Exit as X+import System.PosixCompat.Files as X import Utility.Misc as X import Utility.Exception as X@@ -28,8 +29,8 @@ import Utility.Monad as X import Utility.Data as X import Utility.Applicative as X-import Utility.PosixFiles as X hiding (fileSize) import Utility.FileSize as X import Utility.Split as X+import Utility.FileSystemEncoding as X import Utility.PartialPrelude as X
− GPL
@@ -1,674 +0,0 @@- GNU GENERAL PUBLIC LICENSE- Version 3, 29 June 2007-- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>- Everyone is permitted to copy and distribute verbatim copies- of this license document, but changing it is not allowed.-- Preamble-- The GNU General Public License is a free, copyleft license for-software and other kinds of works.-- The licenses for most software and other practical works are designed-to take away your freedom to share and change the works. By contrast,-the GNU General Public License is intended to guarantee your freedom to-share and change all versions of a program--to make sure it remains free-software for all its users. We, the Free Software Foundation, use the-GNU General Public License for most of our software; it applies also to-any other work released this way by its authors. You can apply it to-your programs, too.-- When we speak of free software, we are referring to freedom, not-price. Our General Public Licenses are designed to make sure that you-have the freedom to distribute copies of free software (and charge for-them if you wish), that you receive source code or can get it if you-want it, that you can change the software or use pieces of it in new-free programs, and that you know you can do these things.-- To protect your rights, we need to prevent others from denying you-these rights or asking you to surrender the rights. Therefore, you have-certain responsibilities if you distribute copies of the software, or if-you modify it: responsibilities to respect the freedom of others.-- For example, if you distribute copies of such a program, whether-gratis or for a fee, you must pass on to the recipients the same-freedoms that you received. You must make sure that they, too, receive-or can get the source code. And you must show them these terms so they-know their rights.-- Developers that use the GNU GPL protect your rights with two steps:-(1) assert copyright on the software, and (2) offer you this License-giving you legal permission to copy, distribute and/or modify it.-- For the developers' and authors' protection, the GPL clearly explains-that there is no warranty for this free software. For both users' and-authors' sake, the GPL requires that modified versions be marked as-changed, so that their problems will not be attributed erroneously to-authors of previous versions.-- Some devices are designed to deny users access to install or run-modified versions of the software inside them, although the manufacturer-can do so. This is fundamentally incompatible with the aim of-protecting users' freedom to change the software. The systematic-pattern of such abuse occurs in the area of products for individuals to-use, which is precisely where it is most unacceptable. Therefore, we-have designed this version of the GPL to prohibit the practice for those-products. If such problems arise substantially in other domains, we-stand ready to extend this provision to those domains in future versions-of the GPL, as needed to protect the freedom of users.-- Finally, every program is threatened constantly by software patents.-States should not allow patents to restrict development and use of-software on general-purpose computers, but in those that do, we wish to-avoid the special danger that patents applied to a free program could-make it effectively proprietary. To prevent this, the GPL assures that-patents cannot be used to render the program non-free.-- The precise terms and conditions for copying, distribution and-modification follow.-- TERMS AND CONDITIONS-- 0. Definitions.-- "This License" refers to version 3 of the GNU General Public License.-- "Copyright" also means copyright-like laws that apply to other kinds of-works, such as semiconductor masks.-- "The Program" refers to any copyrightable work licensed under this-License. Each licensee is addressed as "you". "Licensees" and-"recipients" may be individuals or organizations.-- To "modify" a work means to copy from or adapt all or part of the work-in a fashion requiring copyright permission, other than the making of an-exact copy. The resulting work is called a "modified version" of the-earlier work or a work "based on" the earlier work.-- A "covered work" means either the unmodified Program or a work based-on the Program.-- To "propagate" a work means to do anything with it that, without-permission, would make you directly or secondarily liable for-infringement under applicable copyright law, except executing it on a-computer or modifying a private copy. Propagation includes copying,-distribution (with or without modification), making available to the-public, and in some countries other activities as well.-- To "convey" a work means any kind of propagation that enables other-parties to make or receive copies. Mere interaction with a user through-a computer network, with no transfer of a copy, is not conveying.-- An interactive user interface displays "Appropriate Legal Notices"-to the extent that it includes a convenient and prominently visible-feature that (1) displays an appropriate copyright notice, and (2)-tells the user that there is no warranty for the work (except to the-extent that warranties are provided), that licensees may convey the-work under this License, and how to view a copy of this License. If-the interface presents a list of user commands or options, such as a-menu, a prominent item in the list meets this criterion.-- 1. Source Code.-- The "source code" for a work means the preferred form of the work-for making modifications to it. "Object code" means any non-source-form of a work.-- A "Standard Interface" means an interface that either is an official-standard defined by a recognized standards body, or, in the case of-interfaces specified for a particular programming language, one that-is widely used among developers working in that language.-- The "System Libraries" of an executable work include anything, other-than the work as a whole, that (a) is included in the normal form of-packaging a Major Component, but which is not part of that Major-Component, and (b) serves only to enable use of the work with that-Major Component, or to implement a Standard Interface for which an-implementation is available to the public in source code form. A-"Major Component", in this context, means a major essential component-(kernel, window system, and so on) of the specific operating system-(if any) on which the executable work runs, or a compiler used to-produce the work, or an object code interpreter used to run it.-- The "Corresponding Source" for a work in object code form means all-the source code needed to generate, install, and (for an executable-work) run the object code and to modify the work, including scripts to-control those activities. However, it does not include the work's-System Libraries, or general-purpose tools or generally available free-programs which are used unmodified in performing those activities but-which are not part of the work. For example, Corresponding Source-includes interface definition files associated with source files for-the work, and the source code for shared libraries and dynamically-linked subprograms that the work is specifically designed to require,-such as by intimate data communication or control flow between those-subprograms and other parts of the work.-- The Corresponding Source need not include anything that users-can regenerate automatically from other parts of the Corresponding-Source.-- The Corresponding Source for a work in source code form is that-same work.-- 2. Basic Permissions.-- All rights granted under this License are granted for the term of-copyright on the Program, and are irrevocable provided the stated-conditions are met. This License explicitly affirms your unlimited-permission to run the unmodified Program. The output from running a-covered work is covered by this License only if the output, given its-content, constitutes a covered work. This License acknowledges your-rights of fair use or other equivalent, as provided by copyright law.-- You may make, run and propagate covered works that you do not-convey, without conditions so long as your license otherwise remains-in force. You may convey covered works to others for the sole purpose-of having them make modifications exclusively for you, or provide you-with facilities for running those works, provided that you comply with-the terms of this License in conveying all material for which you do-not control copyright. Those thus making or running the covered works-for you must do so exclusively on your behalf, under your direction-and control, on terms that prohibit them from making any copies of-your copyrighted material outside their relationship with you.-- Conveying under any other circumstances is permitted solely under-the conditions stated below. Sublicensing is not allowed; section 10-makes it unnecessary.-- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.-- No covered work shall be deemed part of an effective technological-measure under any applicable law fulfilling obligations under article-11 of the WIPO copyright treaty adopted on 20 December 1996, or-similar laws prohibiting or restricting circumvention of such-measures.-- When you convey a covered work, you waive any legal power to forbid-circumvention of technological measures to the extent such circumvention-is effected by exercising rights under this License with respect to-the covered work, and you disclaim any intention to limit operation or-modification of the work as a means of enforcing, against the work's-users, your or third parties' legal rights to forbid circumvention of-technological measures.-- 4. Conveying Verbatim Copies.-- You may convey verbatim copies of the Program's source code as you-receive it, in any medium, provided that you conspicuously and-appropriately publish on each copy an appropriate copyright notice;-keep intact all notices stating that this License and any-non-permissive terms added in accord with section 7 apply to the code;-keep intact all notices of the absence of any warranty; and give all-recipients a copy of this License along with the Program.-- You may charge any price or no price for each copy that you convey,-and you may offer support or warranty protection for a fee.-- 5. Conveying Modified Source Versions.-- You may convey a work based on the Program, or the modifications to-produce it from the Program, in the form of source code under the-terms of section 4, provided that you also meet all of these conditions:-- a) The work must carry prominent notices stating that you modified- it, and giving a relevant date.-- b) The work must carry prominent notices stating that it is- released under this License and any conditions added under section- 7. This requirement modifies the requirement in section 4 to- "keep intact all notices".-- c) You must license the entire work, as a whole, under this- License to anyone who comes into possession of a copy. This- License will therefore apply, along with any applicable section 7- additional terms, to the whole of the work, and all its parts,- regardless of how they are packaged. This License gives no- permission to license the work in any other way, but it does not- invalidate such permission if you have separately received it.-- d) If the work has interactive user interfaces, each must display- Appropriate Legal Notices; however, if the Program has interactive- interfaces that do not display Appropriate Legal Notices, your- work need not make them do so.-- A compilation of a covered work with other separate and independent-works, which are not by their nature extensions of the covered work,-and which are not combined with it such as to form a larger program,-in or on a volume of a storage or distribution medium, is called an-"aggregate" if the compilation and its resulting copyright are not-used to limit the access or legal rights of the compilation's users-beyond what the individual works permit. Inclusion of a covered work-in an aggregate does not cause this License to apply to the other-parts of the aggregate.-- 6. Conveying Non-Source Forms.-- You may convey a covered work in object code form under the terms-of sections 4 and 5, provided that you also convey the-machine-readable Corresponding Source under the terms of this License,-in one of these ways:-- a) Convey the object code in, or embodied in, a physical product- (including a physical distribution medium), accompanied by the- Corresponding Source fixed on a durable physical medium- customarily used for software interchange.-- b) Convey the object code in, or embodied in, a physical product- (including a physical distribution medium), accompanied by a- written offer, valid for at least three years and valid for as- long as you offer spare parts or customer support for that product- model, to give anyone who possesses the object code either (1) a- copy of the Corresponding Source for all the software in the- product that is covered by this License, on a durable physical- medium customarily used for software interchange, for a price no- more than your reasonable cost of physically performing this- conveying of source, or (2) access to copy the- Corresponding Source from a network server at no charge.-- c) Convey individual copies of the object code with a copy of the- written offer to provide the Corresponding Source. This- alternative is allowed only occasionally and noncommercially, and- only if you received the object code with such an offer, in accord- with subsection 6b.-- d) Convey the object code by offering access from a designated- place (gratis or for a charge), and offer equivalent access to the- Corresponding Source in the same way through the same place at no- further charge. You need not require recipients to copy the- Corresponding Source along with the object code. If the place to- copy the object code is a network server, the Corresponding Source- may be on a different server (operated by you or a third party)- that supports equivalent copying facilities, provided you maintain- clear directions next to the object code saying where to find the- Corresponding Source. Regardless of what server hosts the- Corresponding Source, you remain obligated to ensure that it is- available for as long as needed to satisfy these requirements.-- e) Convey the object code using peer-to-peer transmission, provided- you inform other peers where the object code and Corresponding- Source of the work are being offered to the general public at no- charge under subsection 6d.-- A separable portion of the object code, whose source code is excluded-from the Corresponding Source as a System Library, need not be-included in conveying the object code work.-- A "User Product" is either (1) a "consumer product", which means any-tangible personal property which is normally used for personal, family,-or household purposes, or (2) anything designed or sold for incorporation-into a dwelling. In determining whether a product is a consumer product,-doubtful cases shall be resolved in favor of coverage. For a particular-product received by a particular user, "normally used" refers to a-typical or common use of that class of product, regardless of the status-of the particular user or of the way in which the particular user-actually uses, or expects or is expected to use, the product. A product-is a consumer product regardless of whether the product has substantial-commercial, industrial or non-consumer uses, unless such uses represent-the only significant mode of use of the product.-- "Installation Information" for a User Product means any methods,-procedures, authorization keys, or other information required to install-and execute modified versions of a covered work in that User Product from-a modified version of its Corresponding Source. The information must-suffice to ensure that the continued functioning of the modified object-code is in no case prevented or interfered with solely because-modification has been made.-- If you convey an object code work under this section in, or with, or-specifically for use in, a User Product, and the conveying occurs as-part of a transaction in which the right of possession and use of the-User Product is transferred to the recipient in perpetuity or for a-fixed term (regardless of how the transaction is characterized), the-Corresponding Source conveyed under this section must be accompanied-by the Installation Information. But this requirement does not apply-if neither you nor any third party retains the ability to install-modified object code on the User Product (for example, the work has-been installed in ROM).-- The requirement to provide Installation Information does not include a-requirement to continue to provide support service, warranty, or updates-for a work that has been modified or installed by the recipient, or for-the User Product in which it has been modified or installed. Access to a-network may be denied when the modification itself materially and-adversely affects the operation of the network or violates the rules and-protocols for communication across the network.-- Corresponding Source conveyed, and Installation Information provided,-in accord with this section must be in a format that is publicly-documented (and with an implementation available to the public in-source code form), and must require no special password or key for-unpacking, reading or copying.-- 7. Additional Terms.-- "Additional permissions" are terms that supplement the terms of this-License by making exceptions from one or more of its conditions.-Additional permissions that are applicable to the entire Program shall-be treated as though they were included in this License, to the extent-that they are valid under applicable law. If additional permissions-apply only to part of the Program, that part may be used separately-under those permissions, but the entire Program remains governed by-this License without regard to the additional permissions.-- When you convey a copy of a covered work, you may at your option-remove any additional permissions from that copy, or from any part of-it. (Additional permissions may be written to require their own-removal in certain cases when you modify the work.) You may place-additional permissions on material, added by you to a covered work,-for which you have or can give appropriate copyright permission.-- Notwithstanding any other provision of this License, for material you-add to a covered work, you may (if authorized by the copyright holders of-that material) supplement the terms of this License with terms:-- a) Disclaiming warranty or limiting liability differently from the- terms of sections 15 and 16 of this License; or-- b) Requiring preservation of specified reasonable legal notices or- author attributions in that material or in the Appropriate Legal- Notices displayed by works containing it; or-- c) Prohibiting misrepresentation of the origin of that material, or- requiring that modified versions of such material be marked in- reasonable ways as different from the original version; or-- d) Limiting the use for publicity purposes of names of licensors or- authors of the material; or-- e) Declining to grant rights under trademark law for use of some- trade names, trademarks, or service marks; or-- f) Requiring indemnification of licensors and authors of that- material by anyone who conveys the material (or modified versions of- it) with contractual assumptions of liability to the recipient, for- any liability that these contractual assumptions directly impose on- those licensors and authors.-- All other non-permissive additional terms are considered "further-restrictions" within the meaning of section 10. If the Program as you-received it, or any part of it, contains a notice stating that it is-governed by this License along with a term that is a further-restriction, you may remove that term. If a license document contains-a further restriction but permits relicensing or conveying under this-License, you may add to a covered work material governed by the terms-of that license document, provided that the further restriction does-not survive such relicensing or conveying.-- If you add terms to a covered work in accord with this section, you-must place, in the relevant source files, a statement of the-additional terms that apply to those files, or a notice indicating-where to find the applicable terms.-- Additional terms, permissive or non-permissive, may be stated in the-form of a separately written license, or stated as exceptions;-the above requirements apply either way.-- 8. Termination.-- You may not propagate or modify a covered work except as expressly-provided under this License. Any attempt otherwise to propagate or-modify it is void, and will automatically terminate your rights under-this License (including any patent licenses granted under the third-paragraph of section 11).-- However, if you cease all violation of this License, then your-license from a particular copyright holder is reinstated (a)-provisionally, unless and until the copyright holder explicitly and-finally terminates your license, and (b) permanently, if the copyright-holder fails to notify you of the violation by some reasonable means-prior to 60 days after the cessation.-- Moreover, your license from a particular copyright holder is-reinstated permanently if the copyright holder notifies you of the-violation by some reasonable means, this is the first time you have-received notice of violation of this License (for any work) from that-copyright holder, and you cure the violation prior to 30 days after-your receipt of the notice.-- Termination of your rights under this section does not terminate the-licenses of parties who have received copies or rights from you under-this License. If your rights have been terminated and not permanently-reinstated, you do not qualify to receive new licenses for the same-material under section 10.-- 9. Acceptance Not Required for Having Copies.-- You are not required to accept this License in order to receive or-run a copy of the Program. Ancillary propagation of a covered work-occurring solely as a consequence of using peer-to-peer transmission-to receive a copy likewise does not require acceptance. However,-nothing other than this License grants you permission to propagate or-modify any covered work. These actions infringe copyright if you do-not accept this License. Therefore, by modifying or propagating a-covered work, you indicate your acceptance of this License to do so.-- 10. Automatic Licensing of Downstream Recipients.-- Each time you convey a covered work, the recipient automatically-receives a license from the original licensors, to run, modify and-propagate that work, subject to this License. You are not responsible-for enforcing compliance by third parties with this License.-- An "entity transaction" is a transaction transferring control of an-organization, or substantially all assets of one, or subdividing an-organization, or merging organizations. If propagation of a covered-work results from an entity transaction, each party to that-transaction who receives a copy of the work also receives whatever-licenses to the work the party's predecessor in interest had or could-give under the previous paragraph, plus a right to possession of the-Corresponding Source of the work from the predecessor in interest, if-the predecessor has it or can get it with reasonable efforts.-- You may not impose any further restrictions on the exercise of the-rights granted or affirmed under this License. For example, you may-not impose a license fee, royalty, or other charge for exercise of-rights granted under this License, and you may not initiate litigation-(including a cross-claim or counterclaim in a lawsuit) alleging that-any patent claim is infringed by making, using, selling, offering for-sale, or importing the Program or any portion of it.-- 11. Patents.-- A "contributor" is a copyright holder who authorizes use under this-License of the Program or a work on which the Program is based. The-work thus licensed is called the contributor's "contributor version".-- A contributor's "essential patent claims" are all patent claims-owned or controlled by the contributor, whether already acquired or-hereafter acquired, that would be infringed by some manner, permitted-by this License, of making, using, or selling its contributor version,-but do not include claims that would be infringed only as a-consequence of further modification of the contributor version. For-purposes of this definition, "control" includes the right to grant-patent sublicenses in a manner consistent with the requirements of-this License.-- Each contributor grants you a non-exclusive, worldwide, royalty-free-patent license under the contributor's essential patent claims, to-make, use, sell, offer for sale, import and otherwise run, modify and-propagate the contents of its contributor version.-- In the following three paragraphs, a "patent license" is any express-agreement or commitment, however denominated, not to enforce a patent-(such as an express permission to practice a patent or covenant not to-sue for patent infringement). To "grant" such a patent license to a-party means to make such an agreement or commitment not to enforce a-patent against the party.-- If you convey a covered work, knowingly relying on a patent license,-and the Corresponding Source of the work is not available for anyone-to copy, free of charge and under the terms of this License, through a-publicly available network server or other readily accessible means,-then you must either (1) cause the Corresponding Source to be so-available, or (2) arrange to deprive yourself of the benefit of the-patent license for this particular work, or (3) arrange, in a manner-consistent with the requirements of this License, to extend the patent-license to downstream recipients. "Knowingly relying" means you have-actual knowledge that, but for the patent license, your conveying the-covered work in a country, or your recipient's use of the covered work-in a country, would infringe one or more identifiable patents in that-country that you have reason to believe are valid.-- If, pursuant to or in connection with a single transaction or-arrangement, you convey, or propagate by procuring conveyance of, a-covered work, and grant a patent license to some of the parties-receiving the covered work authorizing them to use, propagate, modify-or convey a specific copy of the covered work, then the patent license-you grant is automatically extended to all recipients of the covered-work and works based on it.-- A patent license is "discriminatory" if it does not include within-the scope of its coverage, prohibits the exercise of, or is-conditioned on the non-exercise of one or more of the rights that are-specifically granted under this License. You may not convey a covered-work if you are a party to an arrangement with a third party that is-in the business of distributing software, under which you make payment-to the third party based on the extent of your activity of conveying-the work, and under which the third party grants, to any of the-parties who would receive the covered work from you, a discriminatory-patent license (a) in connection with copies of the covered work-conveyed by you (or copies made from those copies), or (b) primarily-for and in connection with specific products or compilations that-contain the covered work, unless you entered into that arrangement,-or that patent license was granted, prior to 28 March 2007.-- Nothing in this License shall be construed as excluding or limiting-any implied license or other defenses to infringement that may-otherwise be available to you under applicable patent law.-- 12. No Surrender of Others' Freedom.-- If conditions are imposed on you (whether by court order, agreement or-otherwise) that contradict the conditions of this License, they do not-excuse you from the conditions of this License. If you cannot convey a-covered work so as to satisfy simultaneously your obligations under this-License and any other pertinent obligations, then as a consequence you may-not convey it at all. For example, if you agree to terms that obligate you-to collect a royalty for further conveying from those to whom you convey-the Program, the only way you could satisfy both those terms and this-License would be to refrain entirely from conveying the Program.-- 13. Use with the GNU Affero General Public License.-- Notwithstanding any other provision of this License, you have-permission to link or combine any covered work with a work licensed-under version 3 of the GNU Affero General Public License into a single-combined work, and to convey the resulting work. The terms of this-License will continue to apply to the part which is the covered work,-but the special requirements of the GNU Affero General Public License,-section 13, concerning interaction through a network will apply to the-combination as such.-- 14. Revised Versions of this License.-- The Free Software Foundation may publish revised and/or new versions of-the GNU General Public License from time to time. Such new versions will-be similar in spirit to the present version, but may differ in detail to-address new problems or concerns.-- Each version is given a distinguishing version number. If the-Program specifies that a certain numbered version of the GNU General-Public License "or any later version" applies to it, you have the-option of following the terms and conditions either of that numbered-version or of any later version published by the Free Software-Foundation. If the Program does not specify a version number of the-GNU General Public License, you may choose any version ever published-by the Free Software Foundation.-- If the Program specifies that a proxy can decide which future-versions of the GNU General Public License can be used, that proxy's-public statement of acceptance of a version permanently authorizes you-to choose that version for the Program.-- Later license versions may give you additional or different-permissions. However, no additional obligations are imposed on any-author or copyright holder as a result of your choosing to follow a-later version.-- 15. Disclaimer of Warranty.-- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.-- 16. Limitation of Liability.-- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF-SUCH DAMAGES.-- 17. Interpretation of Sections 15 and 16.-- If the disclaimer of warranty and limitation of liability provided-above cannot be given local legal effect according to their terms,-reviewing courts shall apply local law that most closely approximates-an absolute waiver of all civil liability in connection with the-Program, unless a warranty or assumption of liability accompanies a-copy of the Program in return for a fee.-- END OF TERMS AND CONDITIONS-- How to Apply These Terms to Your New Programs-- If you develop a new program, and you want it to be of the greatest-possible use to the public, the best way to achieve this is to make it-free software which everyone can redistribute and change under these terms.-- To do so, attach the following notices to the program. It is safest-to attach them to the start of each source file to most effectively-state the exclusion of warranty; and each file should have at least-the "copyright" line and a pointer to where the full notice is found.-- <one line to give the program's name and a brief idea of what it does.>- Copyright (C) <year> <name of author>-- 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 3 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.-- You should have received a copy of the GNU General Public License- along with this program. If not, see <http://www.gnu.org/licenses/>.--Also add information on how to contact you by electronic and paper mail.-- If the program does terminal interaction, make it output a short-notice like this when it starts in an interactive mode:-- <program> Copyright (C) <year> <name of author>- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.- This is free software, and you are welcome to redistribute it- under certain conditions; type `show c' for details.--The hypothetical commands `show w' and `show c' should show the appropriate-parts of the General Public License. Of course, your program's commands-might be different; for a GUI interface, you would use an "about box".-- You should also get your employer (if you work as a programmer) or school,-if any, to sign a "copyright disclaimer" for the program, if necessary.-For more information on this, and how to apply and follow the GNU GPL, see-<http://www.gnu.org/licenses/>.-- The GNU General Public License does not permit incorporating your program-into proprietary programs. If your program is a subroutine library, you-may consider it more useful to permit linking proprietary applications with-the library. If this is what you want to do, use the GNU Lesser General-Public License instead of this License. But first, please read-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
Git.hs view
@@ -5,7 +5,7 @@ - - Copyright 2010-2012 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} {-# LANGUAGE CPP #-}@@ -51,35 +51,35 @@ repoDescribe :: Repo -> String repoDescribe Repo { remoteName = Just name } = name repoDescribe Repo { location = Url url } = show url-repoDescribe Repo { location = Local { worktree = Just dir } } = dir-repoDescribe Repo { location = Local { gitdir = dir } } = dir-repoDescribe Repo { location = LocalUnknown dir } = dir+repoDescribe Repo { location = Local { worktree = Just dir } } = fromRawFilePath dir+repoDescribe Repo { location = Local { gitdir = dir } } = fromRawFilePath dir+repoDescribe Repo { location = LocalUnknown dir } = fromRawFilePath dir repoDescribe Repo { location = Unknown } = "UNKNOWN" {- Location of the repo, either as a path or url. -} repoLocation :: Repo -> String repoLocation Repo { location = Url url } = show url-repoLocation Repo { location = Local { worktree = Just dir } } = dir-repoLocation Repo { location = Local { gitdir = dir } } = dir-repoLocation Repo { location = LocalUnknown dir } = dir+repoLocation Repo { location = Local { worktree = Just dir } } = fromRawFilePath dir+repoLocation Repo { location = Local { gitdir = dir } } = fromRawFilePath dir+repoLocation Repo { location = LocalUnknown dir } = fromRawFilePath dir repoLocation Repo { location = Unknown } = error "unknown repoLocation" {- Path to a repository. For non-bare, this is the worktree, for bare, - it's the gitdir, and for URL repositories, is the path on the remote - host. -}-repoPath :: Repo -> FilePath-repoPath Repo { location = Url u } = unEscapeString $ uriPath u+repoPath :: Repo -> RawFilePath+repoPath Repo { location = Url u } = toRawFilePath $ unEscapeString $ uriPath u repoPath Repo { location = Local { worktree = Just d } } = d repoPath Repo { location = Local { gitdir = d } } = d repoPath Repo { location = LocalUnknown dir } = dir repoPath Repo { location = Unknown } = error "unknown repoPath" -repoWorkTree :: Repo -> Maybe FilePath+repoWorkTree :: Repo -> Maybe RawFilePath repoWorkTree Repo { location = Local { worktree = Just d } } = Just d repoWorkTree _ = Nothing {- Path to a local repository's .git directory. -}-localGitDir :: Repo -> FilePath+localGitDir :: Repo -> RawFilePath localGitDir Repo { location = Local { gitdir = d } } = d localGitDir _ = error "unknown localGitDir" @@ -132,16 +132,17 @@ attributes :: Repo -> FilePath attributes repo | repoIsLocalBare repo = attributesLocal repo- | otherwise = repoPath repo </> ".gitattributes"+ | otherwise = fromRawFilePath (repoPath repo) </> ".gitattributes" attributesLocal :: Repo -> FilePath-attributesLocal repo = localGitDir repo </> "info" </> "attributes"+attributesLocal repo = fromRawFilePath (localGitDir repo)+ </> "info" </> "attributes" {- Path to a given hook script in a repository, only if the hook exists - and is executable. -} hookPath :: String -> Repo -> IO (Maybe FilePath) hookPath script repo = do- let hook = localGitDir repo </> "hooks" </> script+ let hook = fromRawFilePath (localGitDir repo) </> "hooks" </> script ifM (catchBoolIO $ isexecutable hook) ( return $ Just hook , return Nothing ) where@@ -157,22 +158,22 @@ where torel p = do p' <- relPathCwdToFile p- if null p'- then return "."- else return p'+ return $ if null p' then "." else p' {- Adusts the path to a local Repo using the provided function. -} adjustPath :: (FilePath -> IO FilePath) -> Repo -> IO Repo adjustPath f r@(Repo { location = l@(Local { gitdir = d, worktree = w }) }) = do- d' <- f d- w' <- maybe (pure Nothing) (Just <$$> f) w+ d' <- f' d+ w' <- maybe (pure Nothing) (Just <$$> f') w return $ r { location = l { gitdir = d' , worktree = w' } }+ where+ f' v = toRawFilePath <$> f (fromRawFilePath v) adjustPath f r@(Repo { location = LocalUnknown d }) = do- d' <- f d+ d' <- toRawFilePath <$> f (fromRawFilePath d) return $ r { location = LocalUnknown d' } adjustPath _ r = pure r
Git/Branch.hs view
@@ -2,10 +2,11 @@ - - Copyright 2011 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} {-# LANGUAGE BangPatterns #-}+{-# LANGUAGE OverloadedStrings #-} module Git.Branch where @@ -15,13 +16,14 @@ import Git.Command import qualified Git.Config import qualified Git.Ref-import qualified Git.BuildVersion +import qualified Data.ByteString as B+ {- The currently checked out branch. - - In a just initialized git repo before the first commit, - symbolic-ref will show the master branch, even though that- - branch is not created yet. So, this also looks at show-ref HEAD+ - branch is not created yet. So, this also looks at show-ref - to double-check. -} current :: Repo -> IO (Maybe Branch)@@ -30,19 +32,19 @@ case v of Nothing -> return Nothing Just branch -> - ifM (null <$> pipeReadStrict [Param "show-ref", Param $ fromRef branch] r)+ ifM (B.null <$> pipeReadStrict [Param "show-ref", Param $ fromRef branch] r) ( return Nothing , return v ) {- The current branch, which may not really exist yet. -} currentUnsafe :: Repo -> IO (Maybe Branch)-currentUnsafe r = parse . firstLine+currentUnsafe r = parse . firstLine' <$> pipeReadStrict [Param "symbolic-ref", Param "-q", Param $ fromRef Git.Ref.headRef] r where- parse l- | null l = Nothing- | otherwise = Just $ Git.Ref l+ parse b+ | B.null b = Nothing+ | otherwise = Just $ Git.Ref $ decodeBS b {- Checks if the second branch has any commits not present on the first - branch. -}@@ -54,7 +56,8 @@ where changed' :: Branch -> Branch -> [CommandParam] -> Repo -> IO String-changed' origbranch newbranch extraps repo = pipeReadStrict ps repo+changed' origbranch newbranch extraps repo =+ decodeBS <$> pipeReadStrict ps repo where ps = [ Param "log"@@ -73,7 +76,7 @@ - - This requires there to be a path from the old to the new. -} fastForwardable :: Ref -> Ref -> Repo -> IO Bool-fastForwardable old new repo = not . null <$>+fastForwardable old new repo = not . B.null <$> pipeReadStrict [ Param "log" , Param $ fromRef old ++ ".." ++ fromRef new@@ -125,8 +128,7 @@ {- Prevent signing automatic commits. -} applyCommitMode :: CommitMode -> [CommandParam] -> [CommandParam] applyCommitMode commitmode ps- | commitmode == AutomaticCommit && not (Git.BuildVersion.older "2.0.0") =- Param "--no-gpg-sign" : ps+ | commitmode == AutomaticCommit = Param "--no-gpg-sign" : ps | otherwise = ps {- Some versions of git commit-tree honor commit.gpgsign themselves,@@ -134,8 +136,8 @@ applyCommitModeForCommitTree :: CommitMode -> [CommandParam] -> Repo -> [CommandParam] applyCommitModeForCommitTree commitmode ps r | commitmode == ManualCommit =- case (Git.Config.getMaybe "commit.gpgsign" r) of- Just s | Git.Config.isTrue s == Just True ->+ case Git.Config.getMaybe "commit.gpgsign" r of+ Just s | Git.Config.isTrueFalse' s == Just True -> Param "-S":ps _ -> ps' | otherwise = ps'@@ -162,7 +164,7 @@ commit :: CommitMode -> Bool -> String -> Branch -> [Ref] -> Repo -> IO (Maybe Sha) commit commitmode allowempty message branch parentrefs repo = do tree <- getSha "write-tree" $- pipeReadStrict [Param "write-tree"] repo+ decodeBS' <$> pipeReadStrict [Param "write-tree"] repo ifM (cancommit tree) ( do sha <- commitTree commitmode message parentrefs tree repo
Git/BuildVersion.hs view
@@ -2,20 +2,20 @@ - - Copyright 2011 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.BuildVersion where import Git.Version-import qualified Build.SysConfig+import qualified BuildInfo {- Using the version it was configured for avoids running git to check its - version, at the cost that upgrading git won't be noticed. - This is only acceptable because it's rare that git's version influences - code's behavior. -} buildVersion :: GitVersion-buildVersion = normalize Build.SysConfig.gitversion+buildVersion = normalize BuildInfo.gitversion older :: String -> Bool older n = buildVersion < normalize n
Git/CatFile.hs view
@@ -1,8 +1,8 @@ {- git cat-file interface -- - Copyright 2011-2016 Joey Hess <id@joeyh.name>+ - Copyright 2011-2019 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.CatFile (@@ -28,20 +28,23 @@ import Data.Char import Numeric import System.Posix.Types+import Text.Read import Common import Git import Git.Sha+import qualified Git.Ref import Git.Command import Git.Types import Git.FilePath+import Git.HashObject import qualified Utility.CoProcess as CoProcess-import Utility.FileSystemEncoding import Utility.Tuple data CatFileHandle = CatFileHandle { catFileProcess :: CoProcess.CoProcessHandle , checkFileProcess :: CoProcess.CoProcessHandle+ , gitRepo :: Repo } catFileStart :: Repo -> IO CatFileHandle@@ -51,6 +54,7 @@ catFileStart' restartable repo = CatFileHandle <$> startp "--batch" <*> startp "--batch-check=%(objectname) %(objecttype) %(objectsize)"+ <*> pure repo where startp p = gitCoProcessStart restartable [ Param "cat-file"@@ -63,13 +67,13 @@ CoProcess.stop (checkFileProcess h) {- Reads a file from a specified branch. -}-catFile :: CatFileHandle -> Branch -> FilePath -> IO L.ByteString+catFile :: CatFileHandle -> Branch -> RawFilePath -> IO L.ByteString catFile h branch file = catObject h $ Ref $- fromRef branch ++ ":" ++ toInternalGitPath file+ fromRef branch ++ ":" ++ fromRawFilePath (toInternalGitPath file) -catFileDetails :: CatFileHandle -> Branch -> FilePath -> IO (Maybe (L.ByteString, Sha, ObjectType))+catFileDetails :: CatFileHandle -> Branch -> RawFilePath -> IO (Maybe (L.ByteString, Sha, ObjectType)) catFileDetails h branch file = catObjectDetails h $ Ref $- fromRef branch ++ ":" ++ toInternalGitPath file+ fromRef branch ++ ":" ++ fromRawFilePath (toInternalGitPath file) {- Uses a running git cat-file read the content of an object. - Objects that do not exist will have "" returned. -}@@ -77,7 +81,7 @@ catObject h object = maybe L.empty fst3 <$> catObjectDetails h object catObjectDetails :: CatFileHandle -> Ref -> IO (Maybe (L.ByteString, Sha, ObjectType))-catObjectDetails h object = query (catFileProcess h) object $ \from -> do+catObjectDetails h object = query (catFileProcess h) object newlinefallback $ \from -> do header <- hGetLine from case parseResp object header of Just (ParsedResp sha size objtype) -> do@@ -91,23 +95,53 @@ c <- hGetChar from when (c /= expected) $ error $ "missing " ++ (show expected) ++ " from git cat-file"+ + -- Slow fallback path for filenames containing newlines.+ newlinefallback = queryObjectType object (gitRepo h) >>= \case+ Nothing -> return Nothing+ Just objtype -> queryContent object (gitRepo h) >>= \case+ Nothing -> return Nothing+ Just content -> do+ -- only the --batch interface allows getting+ -- the sha, so have to re-hash the object+ sha <- hashObject' objtype+ (flip L.hPut content)+ (gitRepo h)+ return (Just (content, sha, objtype)) {- Gets the size and type of an object, without reading its content. -}-catObjectMetaData :: CatFileHandle -> Ref -> IO (Maybe (Integer, ObjectType))-catObjectMetaData h object = query (checkFileProcess h) object $ \from -> do+catObjectMetaData :: CatFileHandle -> Ref -> IO (Maybe (Sha, FileSize, ObjectType))+catObjectMetaData h object = query (checkFileProcess h) object newlinefallback $ \from -> do resp <- hGetLine from case parseResp object resp of- Just (ParsedResp _ size objtype) ->- return $ Just (size, objtype)+ Just (ParsedResp sha size objtype) ->+ return $ Just (sha, size, objtype) Just DNE -> return Nothing Nothing -> error $ "unknown response from git cat-file " ++ show (resp, object)+ where+ -- Slow fallback path for filenames containing newlines.+ newlinefallback = do+ sha <- Git.Ref.sha object (gitRepo h)+ sz <- querySize object (gitRepo h)+ objtype <- queryObjectType object (gitRepo h)+ return $ (,,) <$> sha <*> sz <*> objtype -data ParsedResp = ParsedResp Sha Integer ObjectType | DNE+data ParsedResp = ParsedResp Sha FileSize ObjectType | DNE -query :: CoProcess.CoProcessHandle -> Ref -> (Handle -> IO a) -> IO a-query hdl object receive = CoProcess.query hdl send receive+query :: CoProcess.CoProcessHandle -> Ref -> IO a -> (Handle -> IO a) -> IO a+query hdl object newlinefallback receive+ -- git cat-file --batch uses a line based protocol, so when the+ -- filename itself contains a newline, have to fall back to another+ -- method of getting the information.+ | '\n' `elem` s = newlinefallback+ -- git strips carriage return from the end of a line, out of some+ -- misplaced desire to support windows, so also use the newline+ -- fallback for those.+ | "\r" `isSuffixOf` s = newlinefallback+ | otherwise = CoProcess.query hdl send receive where- send to = hPutStrLn to (fromRef object)+ send to = hPutStrLn to s+ s = fromRef object parseResp :: Ref -> String -> Maybe ParsedResp parseResp object l @@ -116,13 +150,50 @@ | otherwise = case words l of [sha, objtype, size] | length sha == shaSize ->- case (readObjectType objtype, reads size) of+ case (readObjectType (encodeBS objtype), reads size) of (Just t, [(bytes, "")]) -> Just $ ParsedResp (Ref sha) bytes t _ -> Nothing | otherwise -> Nothing _ -> Nothing +querySingle :: CommandParam -> Ref -> Repo -> (Handle -> IO a) -> IO (Maybe a)+querySingle o r repo reader = assertLocal repo $+ -- In non-batch mode, git cat-file warns on stderr when+ -- asked for an object that does not exist.+ -- Squelch that warning to behave the same as batch mode.+ withNullHandle $ \nullh -> do+ let p = gitCreateProcess + [ Param "cat-file"+ , o+ , Param (fromRef r)+ ] repo+ let p' = p+ { std_err = UseHandle nullh+ , std_in = Inherit+ , std_out = CreatePipe+ }+ pid <- createProcess p'+ let h = stdoutHandle pid+ output <- reader h+ hClose h+ ifM (checkSuccessProcess (processHandle pid))+ ( return (Just output)+ , return Nothing+ )++querySize :: Ref -> Repo -> IO (Maybe FileSize)+querySize r repo = maybe Nothing (readMaybe . takeWhile (/= '\n'))+ <$> querySingle (Param "-s") r repo hGetContentsStrict++queryObjectType :: Ref -> Repo -> IO (Maybe ObjectType)+queryObjectType r repo = maybe Nothing (readObjectType . encodeBS . takeWhile (/= '\n'))+ <$> querySingle (Param "-t") r repo hGetContentsStrict++queryContent :: Ref -> Repo -> IO (Maybe L.ByteString)+queryContent r repo = fmap (\b -> L.fromChunks [b])+ <$> querySingle (Param "-p") r repo S.hGetContents+ {- Gets a list of files and directories in a tree. (Not recursive.) -} catTree :: CatFileHandle -> Ref -> IO [(FilePath, FileMode)] catTree h treeref = go <$> catObjectDetails h treeref@@ -141,7 +212,7 @@ dropsha = L.drop 21 parsemodefile b = - let (modestr, file) = separate (== ' ') (decodeBS b)+ let (modestr, file) = separate (== ' ') (decodeBL b) in (file, readmode modestr) readmode = fromMaybe 0 . fmap fst . headMaybe . readOct
Git/Command.hs view
@@ -2,7 +2,7 @@ - - Copyright 2010-2013 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} {-# LANGUAGE CPP #-}@@ -14,6 +14,9 @@ import Git.Types import qualified Utility.CoProcess as CoProcess +import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString as S+ {- Constructs a git command line operating on the specified repo. -} gitCommandLine :: [CommandParam] -> Repo -> [CommandParam] gitCommandLine params r@(Repo { location = l@(Local { } ) }) =@@ -21,10 +24,10 @@ where setdir | gitEnvOverridesGitDir r = []- | otherwise = [Param $ "--git-dir=" ++ gitdir l]+ | otherwise = [Param $ "--git-dir=" ++ fromRawFilePath (gitdir l)] settree = case worktree l of Nothing -> []- Just t -> [Param $ "--work-tree=" ++ t]+ Just t -> [Param $ "--work-tree=" ++ fromRawFilePath t] gitCommandLine _ repo = assertLocal repo $ error "internal" {- Runs git in the specified repo. -}@@ -47,13 +50,13 @@ {- Runs a git command and returns its output, lazily. - - Also returns an action that should be used when the output is all- - read (or no more is needed), that will wait on the command, and+ - read, that will wait on the command, and - return True if it succeeded. Failure to wait will result in zombies. -}-pipeReadLazy :: [CommandParam] -> Repo -> IO (String, IO Bool)+pipeReadLazy :: [CommandParam] -> Repo -> IO (L.ByteString, IO Bool) pipeReadLazy params repo = assertLocal repo $ do (_, Just h, _, pid) <- createProcess p { std_out = CreatePipe }- c <- hGetContents h+ c <- L.hGetContents h return (c, checkSuccessProcess pid) where p = gitCreateProcess params repo@@ -62,10 +65,14 @@ - - Nonzero exit status is ignored. -}-pipeReadStrict :: [CommandParam] -> Repo -> IO String-pipeReadStrict params repo = assertLocal repo $+pipeReadStrict :: [CommandParam] -> Repo -> IO S.ByteString+pipeReadStrict = pipeReadStrict' S.hGetContents++{- The reader action must be strict. -}+pipeReadStrict' :: (Handle -> IO a) -> [CommandParam] -> Repo -> IO a+pipeReadStrict' reader params repo = assertLocal repo $ withHandle StdoutHandle (createProcessChecked ignoreFailureProcess) p $ \h -> do- output <- hGetContentsStrict h+ output <- reader h hClose h return output where@@ -83,27 +90,35 @@ {- Runs a git command, feeding it input on a handle with an action. -} pipeWrite :: [CommandParam] -> Repo -> (Handle -> IO ()) -> IO ()-pipeWrite params repo = withHandle StdinHandle createProcessSuccess $- gitCreateProcess params repo+pipeWrite params repo = assertLocal repo $ + withHandle StdinHandle createProcessSuccess $+ gitCreateProcess params repo {- Reads null terminated output of a git command (as enabled by the -z - parameter), and splits it. -}-pipeNullSplit :: [CommandParam] -> Repo -> IO ([String], IO Bool)+pipeNullSplit :: [CommandParam] -> Repo -> IO ([L.ByteString], IO Bool) pipeNullSplit params repo = do (s, cleanup) <- pipeReadLazy params repo- return (filter (not . null) $ splitc sep s, cleanup)- where- sep = '\0'+ return (filter (not . L.null) $ L.split 0 s, cleanup) -pipeNullSplitStrict :: [CommandParam] -> Repo -> IO [String]+{- Reads lazily, but copies each part to a strict ByteString for+ - convenience.+ -}+pipeNullSplit' :: [CommandParam] -> Repo -> IO ([S.ByteString], IO Bool)+pipeNullSplit' params repo = do+ (s, cleanup) <- pipeNullSplit params repo+ return (map L.toStrict s, cleanup)++pipeNullSplitStrict :: [CommandParam] -> Repo -> IO [S.ByteString] pipeNullSplitStrict params repo = do s <- pipeReadStrict params repo- return $ filter (not . null) $ splitc sep s- where- sep = '\0'+ return $ filter (not . S.null) $ S.split 0 s -pipeNullSplitZombie :: [CommandParam] -> Repo -> IO [String]+pipeNullSplitZombie :: [CommandParam] -> Repo -> IO [L.ByteString] pipeNullSplitZombie params repo = leaveZombie <$> pipeNullSplit params repo++pipeNullSplitZombie' :: [CommandParam] -> Repo -> IO [S.ByteString]+pipeNullSplitZombie' params repo = leaveZombie <$> pipeNullSplit' params repo {- Doesn't run the cleanup action. A zombie results. -} leaveZombie :: (a, IO Bool) -> a
Git/Config.hs view
@@ -1,32 +1,37 @@ {- git repository configuration handling -- - Copyright 2010-2012 Joey Hess <id@joeyh.name>+ - Copyright 2010-2019 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} +{-# LANGUAGE OverloadedStrings #-}+ module Git.Config where import qualified Data.Map as M+import qualified Data.ByteString as S+import qualified Data.ByteString.Char8 as S8 import Data.Char+import qualified System.FilePath.ByteString as P import Common import Git import Git.Types-import qualified Git.Construct import qualified Git.Command+import qualified Git.Construct import Utility.UserInfo -{- Returns a single git config setting, or a default value if not set. -}-get :: String -> String -> Repo -> String-get key defaultValue repo = M.findWithDefault defaultValue key (config repo)+{- Returns a single git config setting, or a fallback value if not set. -}+get :: ConfigKey -> ConfigValue -> Repo -> ConfigValue+get key fallback repo = M.findWithDefault fallback key (config repo) -{- Returns a list with each line of a multiline config setting. -}-getList :: String -> Repo -> [String]+{- Returns a list of values. -}+getList :: ConfigKey -> Repo -> [ConfigValue] getList key repo = M.findWithDefault [] key (fullconfig repo) {- Returns a single git config setting, if set. -}-getMaybe :: String -> Repo -> Maybe String+getMaybe :: ConfigKey -> Repo -> Maybe ConfigValue getMaybe key repo = M.lookup key (config repo) {- Runs git config and populates a repo with its config.@@ -57,7 +62,7 @@ where params = ["config", "--null", "--list"] p = (proc "git" params)- { cwd = Just d+ { cwd = Just (fromRawFilePath d) , env = gitEnv repo } @@ -79,23 +84,29 @@ {- Reads git config from a handle and populates a repo with it. -} hRead :: Repo -> Handle -> IO Repo hRead repo h = do- val <- hGetContentsStrict h+ val <- S.hGetContents h store val repo {- Stores a git config into a Repo, returning the new version of the Repo. - The git config may be multiple lines, or a single line. - Config settings can be updated incrementally. -}-store :: String -> Repo -> IO Repo+store :: S.ByteString -> Repo -> IO Repo store s repo = do let c = parse s- repo' <- updateLocation $ repo+ updateLocation $ repo { config = (M.map Prelude.head c) `M.union` config repo , fullconfig = M.unionWith (++) c (fullconfig repo) }- rs <- Git.Construct.fromRemotes repo'- return $ repo' { remotes = rs } +{- Stores a single config setting in a Repo, returning the new version of+ - the Repo. Config settings can be updated incrementally. -}+store' :: ConfigKey -> ConfigValue -> Repo -> Repo+store' k v repo = repo+ { config = M.singleton k v `M.union` config repo+ , fullconfig = M.unionWith (++) (M.singleton k [v]) (fullconfig repo)+ }+ {- Updates the location of a repo, based on its configuration. - - Git.Construct makes LocalUknown repos, of which only a directory is@@ -104,13 +115,13 @@ -} updateLocation :: Repo -> IO Repo updateLocation r@(Repo { location = LocalUnknown d })- | isBare r = ifM (doesDirectoryExist dotgit)+ | isBare r = ifM (doesDirectoryExist (fromRawFilePath dotgit)) ( updateLocation' r $ Local dotgit Nothing , updateLocation' r $ Local d Nothing ) | otherwise = updateLocation' r $ Local dotgit (Just d) where- dotgit = (d </> ".git")+ dotgit = d P.</> ".git" updateLocation r@(Repo { location = l@(Local {}) }) = updateLocation' r l updateLocation r = return r @@ -118,52 +129,66 @@ updateLocation' r l = do l' <- case getMaybe "core.worktree" r of Nothing -> return l- Just d -> do+ Just (ConfigValue d) -> do {- core.worktree is relative to the gitdir -}- top <- absPath $ gitdir l- return $ l { worktree = Just $ absPathFrom top d }+ top <- absPath $ fromRawFilePath (gitdir l)+ let p = absPathFrom top (fromRawFilePath d)+ return $ l { worktree = Just (toRawFilePath p) } return $ r { location = l' } {- Parses git config --list or git config --null --list output into a - config map. -}-parse :: String -> M.Map String [String]-parse [] = M.empty+parse :: S.ByteString -> M.Map ConfigKey [ConfigValue] parse s- -- --list output will have an = in the first line- | all ('=' `elem`) (take 1 ls) = sep '=' ls+ | S.null s = M.empty+ -- --list output will have a '=' in the first line+ -- (The first line of --null --list output is the name of a key,+ -- which is assumed to never contain '='.)+ | S.elem eq firstline = sep eq $ S.split nl s -- --null --list output separates keys from values with newlines- | otherwise = sep '\n' $ splitc '\0' s+ | otherwise = sep nl $ S.split 0 s where- ls = lines s- sep c = M.fromListWith (++) . map (\(k,v) -> (k, [v])) .- map (separate (== c))+ nl = fromIntegral (ord '\n')+ eq = fromIntegral (ord '=')+ firstline = S.takeWhile (/= nl) s -{- Checks if a string from git config is a true value. -}-isTrue :: String -> Maybe Bool-isTrue s+ sep c = M.fromListWith (++)+ . map (\(k,v) -> (ConfigKey k, [ConfigValue (S.drop 1 v)])) + . map (S.break (== c))++{- Checks if a string from git config is a true/false value. -}+isTrueFalse :: String -> Maybe Bool+isTrueFalse = isTrueFalse' . ConfigValue . encodeBS'++isTrueFalse' :: ConfigValue -> Maybe Bool+isTrueFalse' (ConfigValue s) | s' == "true" = Just True | s' == "false" = Just False | otherwise = Nothing where- s' = map toLower s+ s' = S8.map toLower s boolConfig :: Bool -> String boolConfig True = "true" boolConfig False = "false" +boolConfig' :: Bool -> S.ByteString+boolConfig' True = "true"+boolConfig' False = "false"+ isBare :: Repo -> Bool-isBare r = fromMaybe False $ isTrue =<< getMaybe coreBare r+isBare r = fromMaybe False $ isTrueFalse' =<< getMaybe coreBare r -coreBare :: String+coreBare :: ConfigKey coreBare = "core.bare" {- Runs a command to get the configuration of a repo, - and returns a repo populated with the configuration, as well as the raw - output of the command. -}-fromPipe :: Repo -> String -> [CommandParam] -> IO (Either SomeException (Repo, String))+fromPipe :: Repo -> String -> [CommandParam] -> IO (Either SomeException (Repo, S.ByteString)) fromPipe r cmd params = try $ withHandle StdoutHandle createProcessSuccess p $ \h -> do- val <- hGetContentsStrict h+ val <- S.hGetContents h r' <- store val r return (r', val) where@@ -171,7 +196,7 @@ {- Reads git config from a specified file and returns the repo populated - with the configuration. -}-fromFile :: Repo -> FilePath -> IO (Either SomeException (Repo, String))+fromFile :: Repo -> FilePath -> IO (Either SomeException (Repo, S.ByteString)) fromFile r f = fromPipe r "git" [ Param "config" , Param "--file"@@ -181,13 +206,13 @@ {- Changes a git config setting in the specified config file. - (Creates the file if it does not already exist.) -}-changeFile :: FilePath -> String -> String -> IO Bool-changeFile f k v = boolSystem "git"+changeFile :: FilePath -> ConfigKey -> S.ByteString -> IO Bool+changeFile f (ConfigKey k) v = boolSystem "git" [ Param "config" , Param "--file" , File f- , Param k- , Param v+ , Param (decodeBS' k)+ , Param (decodeBS' v) ] {- Unsets a git config setting, in both the git repo,@@ -196,10 +221,10 @@ - If unsetting the config fails, including in a read-only repo, or - when the config is not set, returns Nothing. -}-unset :: String -> Repo -> IO (Maybe Repo)-unset k r = ifM (Git.Command.runBool ps r)- ( return $ Just $ r { config = M.delete k (config r) }+unset :: ConfigKey -> Repo -> IO (Maybe Repo)+unset ck@(ConfigKey k) r = ifM (Git.Command.runBool ps r)+ ( return $ Just $ r { config = M.delete ck (config r) } , return Nothing ) where- ps = [Param "config", Param "--unset-all", Param k]+ ps = [Param "config", Param "--unset-all", Param (decodeBS' k)]
Git/Construct.hs view
@@ -2,7 +2,7 @@ - - Copyright 2010-2012 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} {-# LANGUAGE CPP #-}@@ -58,11 +58,11 @@ - specified. -} fromAbsPath :: FilePath -> IO Repo fromAbsPath dir- | absoluteGitPath dir = hunt+ | absoluteGitPath (encodeBS dir) = hunt | otherwise = error $ "internal error, " ++ dir ++ " is not absolute" where- ret = pure . newFrom . LocalUnknown+ ret = pure . newFrom . LocalUnknown . toRawFilePath canondir = dropTrailingPathSeparator dir {- When dir == "foo/.git", git looks for "foo/.git/.git", - and failing that, uses "foo" as the repository. -}@@ -117,7 +117,7 @@ [ Url.scheme reference , "//" , auth- , repoPath r+ , fromRawFilePath (repoPath r) ] in r { location = Url $ fromJust $ parseURI absurl } @@ -127,9 +127,8 @@ where filterconfig f = filter f $ M.toList $ config repo filterkeys f = filterconfig (\(k,_) -> f k)- remotepairs = filterkeys isremote- isremote k = "remote." `isPrefixOf` k && ".url" `isSuffixOf` k- construct (k,v) = remoteNamedFromKey k $ fromRemoteLocation v repo+ remotepairs = filterkeys isRemoteKey+ construct (k,v) = remoteNamedFromKey k (fromRemoteLocation (fromConfigValue v) repo) {- Sets the name of a remote when constructing the Repo to represent it. -} remoteNamed :: String -> IO Repo -> IO Repo@@ -139,11 +138,8 @@ {- Sets the name of a remote based on the git config key, such as - "remote.foo.url". -}-remoteNamedFromKey :: String -> IO Repo -> IO Repo-remoteNamedFromKey k = remoteNamed basename- where- basename = intercalate "." $ - reverse $ drop 1 $ reverse $ drop 1 $ splitc '.' k+remoteNamedFromKey :: ConfigKey -> IO Repo -> IO Repo+remoteNamedFromKey = remoteNamed . remoteKeyToRemoteName {- Constructs a new Repo for one of a Repo's remotes using a given - location (ie, an url). -}@@ -158,7 +154,7 @@ fromRemotePath :: FilePath -> Repo -> IO Repo fromRemotePath dir repo = do dir' <- expandTilde dir- fromPath $ repoPath repo </> dir'+ fromPath $ fromRawFilePath (repoPath repo) </> dir' {- Git remotes can have a directory that is specified relative - to the user's home directory, or that contains tilde expansions.@@ -208,20 +204,29 @@ where check test cont = maybe cont (return . Just) =<< test checkdir c = ifM c- ( return $ Just $ LocalUnknown dir+ ( return $ Just $ LocalUnknown $ toRawFilePath dir , return Nothing )- isRepo = checkdir $ gitSignature $ ".git" </> "config"+ isRepo = checkdir $ + gitSignature (".git" </> "config")+ <||>+ -- A git-worktree lacks .git/config, but has .git/commondir.+ -- (Normally the .git is a file, not a symlink, but it can+ -- be converted to a symlink and git will still work;+ -- this handles that case.)+ gitSignature (".git" </> "gitdir") isBareRepo = checkdir $ gitSignature "config" <&&> doesDirectoryExist (dir </> "objects") gitDirFile = do+ -- git-submodule, git-worktree, and --separate-git-dir+ -- make .git be a file pointing to the real git directory. c <- firstLine <$> catchDefaultIO "" (readFile $ dir </> ".git") return $ if gitdirprefix `isPrefixOf` c then Just $ Local - { gitdir = absPathFrom dir $+ { gitdir = toRawFilePath $ absPathFrom dir $ drop (length gitdirprefix) c- , worktree = Just dir+ , worktree = Just (toRawFilePath dir) } else Nothing where@@ -233,7 +238,6 @@ { location = l , config = M.empty , fullconfig = M.empty- , remotes = [] , remoteName = Nothing , gitEnv = Nothing , gitEnvOverridesGitDir = False
Git/CurrentRepo.hs view
@@ -2,7 +2,7 @@ - - Copyright 2012 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.CurrentRepo where@@ -12,6 +12,7 @@ import Git.Construct import qualified Git.Config import Utility.Env+import Utility.Env.Set {- Gets the current git repository. -@@ -24,12 +25,20 @@ - directory if necessary to ensure it is within the repository's work - tree. While not needed for git commands, this is useful for anything - else that looks for files in the worktree.+ -+ - Also works around a git bug when running some hooks. It+ - runs the hooks in the top of the repository, but if GIT_WORK_TREE+ - was relative (but not "."), it then points to the wrong directory.+ - In this situation GIT_PREFIX contains the directory that + - GIT_WORK_TREE is relative to. -} get :: IO Repo get = do- gd <- pathenv "GIT_DIR"+ gd <- getpathenv "GIT_DIR" r <- configure gd =<< fromCwd- wt <- maybe (worktree $ location r) Just <$> pathenv "GIT_WORK_TREE"+ prefix <- getpathenv "GIT_PREFIX"+ wt <- maybe (fromRawFilePath <$> worktree (location r)) Just+ <$> getpathenvprefix "GIT_WORK_TREE" prefix case wt of Nothing -> return r Just d -> do@@ -38,22 +47,39 @@ setCurrentDirectory d return $ addworktree wt r where- pathenv s = do+ getpathenv s = do v <- getEnv s case v of Just d -> do unsetEnv s- Just <$> absPath d+ return (Just d) Nothing -> return Nothing+ + getpathenvprefix s (Just prefix) | not (null prefix) =+ getpathenv s >>= \case+ Nothing -> return Nothing+ Just d+ | d == "." -> return (Just d)+ | otherwise -> Just <$> absPath (prefix </> d)+ getpathenvprefix s _ = getpathenv s configure Nothing (Just r) = Git.Config.read r configure (Just d) _ = do absd <- absPath d curr <- getCurrentDirectory- Git.Config.read $ newFrom $- Local { gitdir = absd, worktree = Just curr }+ r <- Git.Config.read $ newFrom $+ Local+ { gitdir = toRawFilePath absd+ , worktree = Just (toRawFilePath curr)+ }+ return $ if Git.Config.isBare r+ then r { location = (location r) { worktree = Nothing } }+ else r+ configure Nothing Nothing = giveup "Not in a git repository." - addworktree w r = changelocation r $- Local { gitdir = gitdir (location r), worktree = w }+ addworktree w r = changelocation r $ Local+ { gitdir = gitdir (location r)+ , worktree = fmap toRawFilePath w+ } changelocation r l = r { location = l }
Git/Destroyer.hs view
@@ -4,7 +4,7 @@ - - Copyright 2013, 2014 Joey Hess <joey@kitenet.net> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.Destroyer (@@ -83,7 +83,7 @@ applyDamage :: [Damage] -> Repo -> IO () applyDamage ds r = do contents <- sort . filter (not . skipped)- <$> dirContentsRecursive (localGitDir r)+ <$> dirContentsRecursive (fromRawFilePath (localGitDir r)) forM_ ds $ \d -> do let withfile s a = do let f = selectFile contents s
Git/DiffTreeItem.hs view
@@ -2,7 +2,7 @@ - - Copyright 2012 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.DiffTreeItem (
Git/FilePath.hs view
@@ -5,12 +5,14 @@ - top of the repository even when run in a subdirectory. Adding some - types helps keep that straight. -- - Copyright 2012-2013 Joey Hess <id@joeyh.name>+ - Copyright 2012-2019 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} {-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-} module Git.FilePath ( TopFilePath,@@ -29,30 +31,39 @@ import Common import Git -import qualified System.FilePath.Posix+import qualified System.FilePath.ByteString as P+import qualified System.FilePath.Posix.ByteString+import GHC.Generics+import Control.DeepSeq+import qualified Data.ByteString as S -{- A FilePath, relative to the top of the git repository. -}-newtype TopFilePath = TopFilePath { getTopFilePath :: FilePath }- deriving (Show, Eq, Ord)+{- A RawFilePath, relative to the top of the git repository. -}+newtype TopFilePath = TopFilePath { getTopFilePath :: RawFilePath }+ deriving (Show, Eq, Ord, Generic) +instance NFData TopFilePath+ {- A file in a branch or other treeish. -} data BranchFilePath = BranchFilePath Ref TopFilePath+ deriving (Show, Eq, Ord) {- Git uses the branch:file form to refer to a BranchFilePath -}-descBranchFilePath :: BranchFilePath -> String-descBranchFilePath (BranchFilePath b f) = fromRef b ++ ':' : getTopFilePath f+descBranchFilePath :: BranchFilePath -> S.ByteString+descBranchFilePath (BranchFilePath b f) =+ encodeBS' (fromRef b) <> ":" <> getTopFilePath f {- Path to a TopFilePath, within the provided git repo. -}-fromTopFilePath :: TopFilePath -> Git.Repo -> FilePath-fromTopFilePath p repo = combine (repoPath repo) (getTopFilePath p)+fromTopFilePath :: TopFilePath -> Git.Repo -> RawFilePath+fromTopFilePath p repo = P.combine (repoPath repo) (getTopFilePath p) {- The input FilePath can be absolute, or relative to the CWD. -}-toTopFilePath :: FilePath -> Git.Repo -> IO TopFilePath-toTopFilePath file repo = TopFilePath <$> relPathDirToFile (repoPath repo) file+toTopFilePath :: RawFilePath -> Git.Repo -> IO TopFilePath+toTopFilePath file repo = TopFilePath . toRawFilePath+ <$> relPathDirToFile (fromRawFilePath (repoPath repo)) (fromRawFilePath file) -{- The input FilePath must already be relative to the top of the git+{- The input RawFilePath must already be relative to the top of the git - repository -}-asTopFilePath :: FilePath -> TopFilePath+asTopFilePath :: RawFilePath -> TopFilePath asTopFilePath file = TopFilePath file {- Git may use a different representation of a path when storing@@ -62,25 +73,25 @@ - despite Windows using '\'. - -}-type InternalGitPath = String+type InternalGitPath = RawFilePath -toInternalGitPath :: FilePath -> InternalGitPath+toInternalGitPath :: RawFilePath -> InternalGitPath #ifndef mingw32_HOST_OS toInternalGitPath = id #else-toInternalGitPath = replace "\\" "/"+toInternalGitPath = encodeBS . replace "\\" "/" . decodeBS #endif -fromInternalGitPath :: InternalGitPath -> FilePath+fromInternalGitPath :: InternalGitPath -> RawFilePath #ifndef mingw32_HOST_OS fromInternalGitPath = id #else-fromInternalGitPath = replace "/" "\\"+fromInternalGitPath = encodeBS . replace "/" "\\" . decodeBS #endif {- isAbsolute on Windows does not think "/foo" or "\foo" is absolute, - so try posix paths. -}-absoluteGitPath :: FilePath -> Bool-absoluteGitPath p = isAbsolute p ||- System.FilePath.Posix.isAbsolute (toInternalGitPath p)+absoluteGitPath :: RawFilePath -> Bool+absoluteGitPath p = P.isAbsolute p ||+ System.FilePath.Posix.ByteString.isAbsolute (toInternalGitPath p)
Git/Filename.hs view
@@ -3,7 +3,7 @@ - - Copyright 2010, 2011 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.Filename where@@ -12,23 +12,44 @@ import Utility.Format (decode_c, encode_c) import Data.Char+import Data.Word+import qualified Data.ByteString as S -decode :: String -> FilePath-decode [] = []-decode f@(c:s)- -- encoded strings will be inside double quotes- | c == '"' && end s == ['"'] = decode_c $ beginning s- | otherwise = f+-- encoded filenames will be inside double quotes+decode :: S.ByteString -> RawFilePath+decode b = case S.uncons b of+ Nothing -> b+ Just (h, t)+ | h /= q -> b+ | otherwise -> case S.unsnoc t of+ Nothing -> b+ Just (i, l)+ | l /= q -> b+ | otherwise ->+ encodeBS $ decode_c $ decodeBS i+ where+ q :: Word8+ q = fromIntegral (ord '"') {- Should not need to use this, except for testing decode. -}-encode :: FilePath -> String-encode s = "\"" ++ encode_c s ++ "\""+encode :: RawFilePath -> S.ByteString+encode s = encodeBS $ "\"" ++ encode_c (decodeBS s) ++ "\"" -{- For quickcheck. - -- - See comment on Utility.Format.prop_encode_c_decode_c_roundtrip for- - why this only tests chars < 256 -}-prop_encode_decode_roundtrip :: String -> Bool-prop_encode_decode_roundtrip s = s' == decode (encode s')+prop_encode_decode_roundtrip :: FilePath -> Bool+prop_encode_decode_roundtrip s = s' ==+ fromRawFilePath (decode (encode (toRawFilePath s'))) where- s' = filter (\c -> ord c < 256) s+ s' = nonul (nohigh s)+ -- Encoding and then decoding roundtrips only when+ -- the string does not contain high unicode, because eg, + -- both "\12345" and "\227\128\185" are encoded to+ -- "\343\200\271".+ --+ -- This property papers over the problem, by only+ -- testing ascii+ nohigh = filter isAscii+ -- A String can contain a NUL, but toRawFilePath+ -- truncates on the NUL, which is generally fine+ -- because unix filenames cannot contain NUL.+ -- So the encoding only roundtrips when there is no nul.+ nonul = filter (/= '\NUL')
Git/Fsck.hs view
@@ -2,7 +2,7 @@ - - Copyright 2013 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} {-# LANGUAGE BangPatterns #-}@@ -22,10 +22,11 @@ import Git.Command import Git.Sha import Utility.Batch-import qualified Git.Version import qualified Data.Set as S import Control.Concurrent.Async+import qualified Data.Semigroup as Sem+import Prelude data FsckResults = FsckFoundMissing@@ -44,15 +45,21 @@ type Truncated = Bool +appendFsckOutput :: FsckOutput -> FsckOutput -> FsckOutput+appendFsckOutput (FsckOutput s1 t1) (FsckOutput s2 t2) =+ FsckOutput (S.union s1 s2) (t1 || t2)+appendFsckOutput (FsckOutput s t) _ = FsckOutput s t+appendFsckOutput _ (FsckOutput s t) = FsckOutput s t+appendFsckOutput NoFsckOutput NoFsckOutput = NoFsckOutput+appendFsckOutput AllDuplicateEntriesWarning AllDuplicateEntriesWarning = AllDuplicateEntriesWarning+appendFsckOutput AllDuplicateEntriesWarning NoFsckOutput = AllDuplicateEntriesWarning+appendFsckOutput NoFsckOutput AllDuplicateEntriesWarning = AllDuplicateEntriesWarning++instance Sem.Semigroup FsckOutput where+ (<>) = appendFsckOutput+ instance Monoid FsckOutput where mempty = NoFsckOutput- mappend (FsckOutput s1 t1) (FsckOutput s2 t2) = FsckOutput (S.union s1 s2) (t1 || t2)- mappend (FsckOutput s t) _ = FsckOutput s t- mappend _ (FsckOutput s t) = FsckOutput s t- mappend NoFsckOutput NoFsckOutput = NoFsckOutput- mappend AllDuplicateEntriesWarning AllDuplicateEntriesWarning = AllDuplicateEntriesWarning- mappend AllDuplicateEntriesWarning NoFsckOutput = AllDuplicateEntriesWarning- mappend NoFsckOutput AllDuplicateEntriesWarning = AllDuplicateEntriesWarning {- Runs fsck to find some of the broken objects in the repository. - May not find all broken objects, if fsck fails on bad data in some of@@ -65,9 +72,7 @@ -} findBroken :: Bool -> Repo -> IO FsckResults findBroken batchmode r = do- supportsNoDangling <- (>= Git.Version.normalize "1.7.10")- <$> Git.Version.installed- let (command, params) = ("git", fsckParams supportsNoDangling r)+ let (command, params) = ("git", fsckParams r) (command', params') <- if batchmode then toBatchCommand (command, params) else return (command, params)@@ -78,8 +83,8 @@ , std_err = CreatePipe } (o1, o2) <- concurrently- (parseFsckOutput maxobjs r supportsNoDangling (stdoutHandle p))- (parseFsckOutput maxobjs r supportsNoDangling (stderrHandle p))+ (parseFsckOutput maxobjs r (stdoutHandle p))+ (parseFsckOutput maxobjs r (stderrHandle p)) fsckok <- checkSuccessProcess pid case mappend o1 o2 of FsckOutput badobjs truncated@@ -112,15 +117,15 @@ findMissing :: [Sha] -> Repo -> IO MissingObjects findMissing objs r = S.fromList <$> filterM (`isMissing` r) objs -parseFsckOutput :: Int -> Repo -> Bool -> Handle -> IO FsckOutput-parseFsckOutput maxobjs r supportsNoDangling h = do+parseFsckOutput :: Int -> Repo -> Handle -> IO FsckOutput+parseFsckOutput maxobjs r h = do ls <- lines <$> hGetContents h if null ls then return NoFsckOutput else if all ("duplicateEntries" `isInfixOf`) ls then return AllDuplicateEntriesWarning else do- let shas = findShas supportsNoDangling ls+ let shas = findShas ls let !truncated = length shas > maxobjs missingobjs <- findMissing (take maxobjs shas) r return $ FsckOutput missingobjs truncated@@ -133,18 +138,14 @@ , Param (fromRef s) ] r -findShas :: Bool -> [String] -> [Sha]-findShas supportsNoDangling = catMaybes . map extractSha . concat . map words . filter wanted+findShas :: [String] -> [Sha]+findShas = catMaybes . map extractSha . concat . map words . filter wanted where- wanted l- | supportsNoDangling = True- | otherwise = not ("dangling " `isPrefixOf` l)+ wanted l = not ("dangling " `isPrefixOf` l) -fsckParams :: Bool -> Repo -> [CommandParam]-fsckParams supportsNoDangling = gitCommandLine $ map Param $ catMaybes- [ Just "fsck"- , if supportsNoDangling- then Just "--no-dangling"- else Nothing- , Just "--no-reflogs"+fsckParams :: Repo -> [CommandParam]+fsckParams = gitCommandLine $ map Param+ [ "fsck"+ , "--no-dangling"+ , "--no-reflogs" ]
+ Git/HashObject.hs view
@@ -0,0 +1,76 @@+{- git hash-object interface+ -+ - Copyright 2011-2019 Joey Hess <id@joeyh.name>+ -+ - Licensed under the GNU AGPL version 3 or higher.+ -}++{-# LANGUAGE CPP #-}++module Git.HashObject where++import Common+import Git+import Git.Sha+import Git.Command+import Git.Types+import qualified Utility.CoProcess as CoProcess+import Utility.Tmp++import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as L+import Data.ByteString.Builder++type HashObjectHandle = CoProcess.CoProcessHandle++hashObjectStart :: Bool -> Repo -> IO HashObjectHandle+hashObjectStart writeobject = gitCoProcessStart True $ catMaybes+ [ Just (Param "hash-object")+ , if writeobject then Just (Param "-w") else Nothing+ , Just (Param "--stdin-paths")+ , Just (Param "--no-filters")+ ]++hashObjectStop :: HashObjectHandle -> IO ()+hashObjectStop = CoProcess.stop++{- Injects a file into git, returning the Sha of the object. -}+hashFile :: HashObjectHandle -> FilePath -> IO Sha+hashFile h file = CoProcess.query h send receive+ where+ send to = hPutStrLn to =<< absPath file+ receive from = getSha "hash-object" $ hGetLine from++class HashableBlob t where+ hashableBlobToHandle :: Handle -> t -> IO ()++instance HashableBlob L.ByteString where+ hashableBlobToHandle = L.hPut++instance HashableBlob S.ByteString where+ hashableBlobToHandle = S.hPut++instance HashableBlob Builder where+ hashableBlobToHandle = hPutBuilder++{- Injects a blob into git. Unfortunately, the current git-hash-object+ - interface does not allow batch hashing without using temp files. -}+hashBlob :: HashableBlob b => HashObjectHandle -> b -> IO Sha+hashBlob h b = withTmpFile "hash" $ \tmp tmph -> do+ hashableBlobToHandle tmph b+ hClose tmph+ hashFile h tmp++{- Injects some content into git, returning its Sha.+ - + - Avoids using a tmp file, but runs a new hash-object command each+ - time called. -}+hashObject :: ObjectType -> String -> Repo -> IO Sha+hashObject objtype content = hashObject' objtype (flip hPutStr content)++hashObject' :: ObjectType -> (Handle -> IO ()) -> Repo -> IO Sha+hashObject' objtype writer repo = getSha subcmd $+ pipeWriteRead (map Param params) (Just writer) repo+ where+ subcmd = "hash-object"+ params = [subcmd, "-t", decodeBS (fmtObjectType objtype), "-w", "--stdin", "--no-filters"]
Git/Index.hs view
@@ -1,8 +1,8 @@ {- git index file stuff -- - Copyright 2011 Joey Hess <id@joeyh.name>+ - Copyright 2011-2018 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.Index where@@ -10,6 +10,7 @@ import Common import Git import Utility.Env+import Utility.Env.Set indexEnv :: String indexEnv = "GIT_INDEX_FILE"@@ -46,25 +47,14 @@ reset (Just v) = setEnv indexEnv v True reset _ = unsetEnv var +{- The normal index file. Does not check GIT_INDEX_FILE. -} indexFile :: Repo -> FilePath-indexFile r = localGitDir r </> "index"+indexFile r = fromRawFilePath (localGitDir r) </> "index" -{- Git locks the index by creating this file. -}-indexFileLock :: Repo -> FilePath-indexFileLock r = indexFile r ++ ".lock"+{- The index file git will currently use, checking GIT_INDEX_FILE. -}+currentIndexFile :: Repo -> IO FilePath+currentIndexFile r = fromMaybe (indexFile r) <$> getEnv indexEnv -{- When the pre-commit hook is run, and git commit has been run with- - a file or files specified to commit, rather than committing the staged- - index, git provides the pre-commit hook with a "false index file".- -- - Changes made to this index will influence the commit, but won't- - affect the real index file.- -- - This detects when we're in this situation, using a heuristic, which- - might be broken by changes to git. Any use of this should have a test- - case to make sure it works.- -}-haveFalseIndex :: IO Bool-haveFalseIndex = maybe (False) check <$> getEnv indexEnv- where- check f = "next-index" `isPrefixOf` takeFileName f+{- Git locks the index by creating this file. -}+indexFileLock :: FilePath -> FilePath+indexFileLock f = f ++ ".lock"
Git/LsFiles.hs view
@@ -1,13 +1,15 @@ {- git ls-files interface -- - Copyright 2010,2012 Joey Hess <id@joeyh.name>+ - Copyright 2010-2018 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.LsFiles ( inRepo,+ inRepoOrBranch, notInRepo,+ notInRepoIncludingEmptyDirectories, allFiles, deleted, modified,@@ -32,69 +34,89 @@ import Numeric import System.Posix.Types+import qualified Data.ByteString.Lazy as L -{- Scans for files that are checked into git at the specified locations. -}-inRepo :: [FilePath] -> Repo -> IO ([FilePath], IO Bool)-inRepo l = pipeNullSplit $ - Param "ls-files" :- Param "--cached" :- Param "-z" :- Param "--" :- map File l+{- Scans for files that are checked into git's index at the specified locations. -}+inRepo :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+inRepo = inRepo' [] +inRepo' :: [CommandParam] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+inRepo' ps l repo = pipeNullSplit' params repo+ where+ params = + Param "ls-files" :+ Param "--cached" :+ Param "-z" :+ ps +++ (Param "--" : map (File . fromRawFilePath) l)++{- Files that are checked into the index or have been committed to a+ - branch. -}+inRepoOrBranch :: Branch -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+inRepoOrBranch (Ref b) = inRepo' [Param $ "--with-tree=" ++ b]+ {- Scans for files at the specified locations that are not checked into git. -}-notInRepo :: Bool -> [FilePath] -> Repo -> IO ([FilePath], IO Bool)-notInRepo include_ignored l repo = pipeNullSplit params repo+notInRepo :: Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+notInRepo = notInRepo' []++notInRepo' :: [CommandParam] -> Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+notInRepo' ps include_ignored l repo = pipeNullSplit' params repo where params = concat [ [ Param "ls-files", Param "--others"]+ , ps , exclude , [ Param "-z", Param "--" ]- , map File l+ , map (File . fromRawFilePath) l ] exclude | include_ignored = [] | otherwise = [Param "--exclude-standard"] +{- Scans for files at the specified locations that are not checked into+ - git. Empty directories are included in the result. -}+notInRepoIncludingEmptyDirectories :: Bool -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+notInRepoIncludingEmptyDirectories = notInRepo' [Param "--directory"]+ {- Finds all files in the specified locations, whether checked into git or - not. -}-allFiles :: [FilePath] -> Repo -> IO ([FilePath], IO Bool)-allFiles l = pipeNullSplit $+allFiles :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+allFiles l = pipeNullSplit' $ Param "ls-files" : Param "--cached" : Param "--others" : Param "-z" : Param "--" :- map File l+ map (File . fromRawFilePath) l {- Returns a list of files in the specified locations that have been - deleted. -}-deleted :: [FilePath] -> Repo -> IO ([FilePath], IO Bool)-deleted l repo = pipeNullSplit params repo+deleted :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+deleted l repo = pipeNullSplit' params repo where params = Param "ls-files" : Param "--deleted" : Param "-z" : Param "--" :- map File l+ map (File . fromRawFilePath) l {- Returns a list of files in the specified locations that have been - modified. -}-modified :: [FilePath] -> Repo -> IO ([FilePath], IO Bool)-modified l repo = pipeNullSplit params repo+modified :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+modified l repo = pipeNullSplit' params repo where params = Param "ls-files" : Param "--modified" : Param "-z" : Param "--" :- map File l+ map (File . fromRawFilePath) l {- Files that have been modified or are not checked into git (and are not - ignored). -}-modifiedOthers :: [FilePath] -> Repo -> IO ([FilePath], IO Bool)-modifiedOthers l repo = pipeNullSplit params repo+modifiedOthers :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+modifiedOthers l repo = pipeNullSplit' params repo where params = Param "ls-files" :@@ -103,69 +125,69 @@ Param "--exclude-standard" : Param "-z" : Param "--" :- map File l+ map (File . fromRawFilePath) l {- Returns a list of all files that are staged for commit. -}-staged :: [FilePath] -> Repo -> IO ([FilePath], IO Bool)+staged :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool) staged = staged' [] {- Returns a list of the files, staged for commit, that are being added, - moved, or changed (but not deleted), from the specified locations. -}-stagedNotDeleted :: [FilePath] -> Repo -> IO ([FilePath], IO Bool)+stagedNotDeleted :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool) stagedNotDeleted = staged' [Param "--diff-filter=ACMRT"] -staged' :: [CommandParam] -> [FilePath] -> Repo -> IO ([FilePath], IO Bool)-staged' ps l = pipeNullSplit $ prefix ++ ps ++ suffix+staged' :: [CommandParam] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool)+staged' ps l repo = pipeNullSplit' (prefix ++ ps ++ suffix) repo where prefix = [Param "diff", Param "--cached", Param "--name-only", Param "-z"]- suffix = Param "--" : map File l+ suffix = Param "--" : map (File . fromRawFilePath) l -type StagedDetails = (FilePath, Maybe Sha, Maybe FileMode)+type StagedDetails = (RawFilePath, Maybe Sha, Maybe FileMode) {- Returns details about files that are staged in the index, - as well as files not yet in git. Skips ignored files. -}-stagedOthersDetails :: [FilePath] -> Repo -> IO ([StagedDetails], IO Bool)+stagedOthersDetails :: [RawFilePath] -> Repo -> IO ([StagedDetails], IO Bool) stagedOthersDetails = stagedDetails' [Param "--others", Param "--exclude-standard"] {- Returns details about all files that are staged in the index. -}-stagedDetails :: [FilePath] -> Repo -> IO ([StagedDetails], IO Bool)+stagedDetails :: [RawFilePath] -> Repo -> IO ([StagedDetails], IO Bool) stagedDetails = stagedDetails' [] {- Gets details about staged files, including the Sha of their staged - contents. -}-stagedDetails' :: [CommandParam] -> [FilePath] -> Repo -> IO ([StagedDetails], IO Bool)+stagedDetails' :: [CommandParam] -> [RawFilePath] -> Repo -> IO ([StagedDetails], IO Bool) stagedDetails' ps l repo = do (ls, cleanup) <- pipeNullSplit params repo return (map parse ls, cleanup) where params = Param "ls-files" : Param "--stage" : Param "-z" : ps ++ - Param "--" : map File l+ Param "--" : map (File . fromRawFilePath) l parse s- | null file = (s, Nothing, Nothing)- | otherwise = (file, extractSha $ take shaSize rest, readmode mode)+ | null file = (L.toStrict s, Nothing, Nothing)+ | otherwise = (toRawFilePath file, extractSha $ take shaSize rest, readmode mode) where- (metadata, file) = separate (== '\t') s+ (metadata, file) = separate (== '\t') (decodeBL' s) (mode, rest) = separate (== ' ') metadata readmode = fst <$$> headMaybe . readOct {- Returns a list of the files in the specified locations that are staged - for commit, and whose type has changed. -}-typeChangedStaged :: [FilePath] -> Repo -> IO ([FilePath], IO Bool)+typeChangedStaged :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool) typeChangedStaged = typeChanged' [Param "--cached"] {- Returns a list of the files in the specified locations whose type has - changed. Files only staged for commit will not be included. -}-typeChanged :: [FilePath] -> Repo -> IO ([FilePath], IO Bool)+typeChanged :: [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool) typeChanged = typeChanged' [] -typeChanged' :: [CommandParam] -> [FilePath] -> Repo -> IO ([FilePath], IO Bool)+typeChanged' :: [CommandParam] -> [RawFilePath] -> Repo -> IO ([RawFilePath], IO Bool) typeChanged' ps l repo = do (fs, cleanup) <- pipeNullSplit (prefix ++ ps ++ suffix) repo -- git diff returns filenames relative to the top of the git repo; -- convert to filenames relative to the cwd, like git ls-files.- top <- absPath (repoPath repo)+ top <- absPath (fromRawFilePath (repoPath repo)) currdir <- getCurrentDirectory- return (map (\f -> relPathDirToFileAbs currdir $ top </> f) fs, cleanup)+ return (map (\f -> toRawFilePath (relPathDirToFileAbs currdir $ top </> decodeBL' f)) fs, cleanup) where prefix = [ Param "diff"@@ -173,7 +195,7 @@ , Param "--diff-filter=T" , Param "-z" ]- suffix = Param "--" : (if null l then [File "."] else map File l)+ suffix = Param "--" : (if null l then [File "."] else map (File . fromRawFilePath) l) {- A item in conflict has two possible values. - Either can be Nothing, when that side deleted the file. -}@@ -183,10 +205,10 @@ } deriving (Show) data Unmerged = Unmerged- { unmergedFile :: FilePath- , unmergedBlobType :: Conflicting BlobType+ { unmergedFile :: RawFilePath+ , unmergedTreeItemType :: Conflicting TreeItemType , unmergedSha :: Conflicting Sha- } deriving (Show)+ } {- Returns a list of the files in the specified locations that have - unresolved merge conflicts.@@ -198,38 +220,38 @@ - 3 = them - If a line is omitted, that side removed the file. -}-unmerged :: [FilePath] -> Repo -> IO ([Unmerged], IO Bool)+unmerged :: [RawFilePath] -> Repo -> IO ([Unmerged], IO Bool) unmerged l repo = do (fs, cleanup) <- pipeNullSplit params repo- return (reduceUnmerged [] $ catMaybes $ map parseUnmerged fs, cleanup)+ return (reduceUnmerged [] $ catMaybes $ map (parseUnmerged . decodeBL') fs, cleanup) where params = Param "ls-files" : Param "--unmerged" : Param "-z" : Param "--" :- map File l+ map (File . fromRawFilePath) l data InternalUnmerged = InternalUnmerged { isus :: Bool- , ifile :: FilePath- , iblobtype :: Maybe BlobType+ , ifile :: RawFilePath+ , itreeitemtype :: Maybe TreeItemType , isha :: Maybe Sha- } deriving (Show)+ } parseUnmerged :: String -> Maybe InternalUnmerged parseUnmerged s | null file = Nothing | otherwise = case words metadata of- (rawblobtype:rawsha:rawstage:_) -> do+ (rawtreeitemtype:rawsha:rawstage:_) -> do stage <- readish rawstage :: Maybe Int if stage /= 2 && stage /= 3 then Nothing else do- blobtype <- readBlobType rawblobtype+ treeitemtype <- readTreeItemType (encodeBS rawtreeitemtype) sha <- extractSha rawsha- return $ InternalUnmerged (stage == 2) file- (Just blobtype) (Just sha)+ return $ InternalUnmerged (stage == 2) (toRawFilePath file)+ (Just treeitemtype) (Just sha) _ -> Nothing where (metadata, file) = separate (== '\t') s@@ -239,12 +261,12 @@ reduceUnmerged c (i:is) = reduceUnmerged (new:c) rest where (rest, sibi) = findsib i is- (blobtypeA, blobtypeB, shaA, shaB)- | isus i = (iblobtype i, iblobtype sibi, isha i, isha sibi)- | otherwise = (iblobtype sibi, iblobtype i, isha sibi, isha i)+ (treeitemtypeA, treeitemtypeB, shaA, shaB)+ | isus i = (itreeitemtype i, itreeitemtype sibi, isha i, isha sibi)+ | otherwise = (itreeitemtype sibi, itreeitemtype i, isha sibi, isha i) new = Unmerged { unmergedFile = ifile i- , unmergedBlobType = Conflicting blobtypeA blobtypeB+ , unmergedTreeItemType = Conflicting treeitemtypeA treeitemtypeB , unmergedSha = Conflicting shaA shaB } findsib templatei [] = ([], removed templatei)@@ -253,6 +275,6 @@ | otherwise = (l:ls, removed templatei) removed templatei = templatei { isus = not (isus templatei)- , iblobtype = Nothing+ , itreeitemtype = Nothing , isha = Nothing }
Git/LsTree.hs view
@@ -1,19 +1,21 @@ {- git ls-tree interface -- - Copyright 2011-2016 Joey Hess <id@joeyh.name>+ - Copyright 2011-2019 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} {-# LANGUAGE BangPatterns #-} module Git.LsTree ( TreeItem(..),+ LsTreeMode(..), lsTree, lsTree', lsTreeParams, lsTreeFiles, parseLsTree,+ formatLsTree, ) where import Common@@ -22,42 +24,52 @@ import Git.Sha import Git.FilePath import qualified Git.Filename+import Utility.Attoparsec import Numeric-import Data.Char+import Data.Either import System.Posix.Types+import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as L+import qualified Data.Attoparsec.ByteString.Lazy as A+import qualified Data.Attoparsec.ByteString.Char8 as A8 data TreeItem = TreeItem { mode :: FileMode- , typeobj :: String+ , typeobj :: S.ByteString , sha :: Ref , file :: TopFilePath } deriving Show -{- Lists the complete contents of a tree, recursing into sub-trees,- - with lazy output. -}-lsTree :: Ref -> Repo -> IO ([TreeItem], IO Bool)+data LsTreeMode = LsTreeRecursive | LsTreeNonRecursive++{- Lists the contents of a tree, with lazy output. -}+lsTree :: LsTreeMode -> Ref -> Repo -> IO ([TreeItem], IO Bool) lsTree = lsTree' [] -lsTree' :: [CommandParam] -> Ref -> Repo -> IO ([TreeItem], IO Bool)-lsTree' ps t repo = do- (l, cleanup) <- pipeNullSplit (lsTreeParams t ps) repo- return (map parseLsTree l, cleanup)+lsTree' :: [CommandParam] -> LsTreeMode -> Ref -> Repo -> IO ([TreeItem], IO Bool)+lsTree' ps lsmode t repo = do+ (l, cleanup) <- pipeNullSplit (lsTreeParams lsmode t ps) repo+ return (rights (map parseLsTree l), cleanup) -lsTreeParams :: Ref -> [CommandParam] -> [CommandParam]-lsTreeParams r ps =+lsTreeParams :: LsTreeMode -> Ref -> [CommandParam] -> [CommandParam]+lsTreeParams lsmode r ps = [ Param "ls-tree" , Param "--full-tree" , Param "-z"- , Param "-r"- ] ++ ps +++ ] ++ recursiveparams ++ ps ++ [ Param "--" , File $ fromRef r ]+ where+ recursiveparams = case lsmode of+ LsTreeRecursive -> [ Param "-r" ]+ LsTreeNonRecursive -> [] {- Lists specified files in a tree. -} lsTreeFiles :: Ref -> [FilePath] -> Repo -> IO [TreeItem]-lsTreeFiles t fs repo = map parseLsTree <$> pipeNullSplitStrict ps repo+lsTreeFiles t fs repo = rights . map (parseLsTree . L.fromStrict)+ <$> pipeNullSplitStrict ps repo where ps = [ Param "ls-tree"@@ -67,21 +79,34 @@ , File $ fromRef t ] ++ map File fs +parseLsTree :: L.ByteString -> Either String TreeItem+parseLsTree b = case A.parse parserLsTree b of+ A.Done _ r -> Right r+ A.Fail _ _ err -> Left err+ {- Parses a line of ls-tree output, in format: - mode SP type SP sha TAB file - - (The --long format is not currently supported.) -}-parseLsTree :: String -> TreeItem-parseLsTree l = TreeItem - { mode = smode- , typeobj = t- , sha = Ref s- , file = sfile- }- where- (m, past_m) = splitAt 7 l -- mode is 6 bytes- (!t, past_t) = separate isSpace past_m- (!s, past_s) = splitAt shaSize past_t- !f = drop 1 past_s- !smode = fst $ Prelude.head $ readOct m- !sfile = asTopFilePath $ Git.Filename.decode f+parserLsTree :: A.Parser TreeItem+parserLsTree = TreeItem+ -- mode+ <$> octal+ <* A8.char ' '+ -- type+ <*> A.takeTill (== 32)+ <* A8.char ' '+ -- sha+ <*> (Ref . decodeBS' <$> A.take shaSize)+ <* A8.char '\t'+ -- file+ <*> (asTopFilePath . Git.Filename.decode <$> A.takeByteString)++{- Inverse of parseLsTree -}+formatLsTree :: TreeItem -> String+formatLsTree ti = unwords+ [ showOct (mode ti) ""+ , decodeBS (typeobj ti)+ , fromRef (sha ti)+ , fromRawFilePath (getTopFilePath (file ti))+ ]
Git/Objects.hs view
@@ -2,7 +2,7 @@ - - Copyright 2013 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.Objects where@@ -12,7 +12,7 @@ import Git.Sha objectsDir :: Repo -> FilePath-objectsDir r = localGitDir r </> "objects"+objectsDir r = fromRawFilePath (localGitDir r) </> "objects" packDir :: Repo -> FilePath packDir r = objectsDir r </> "pack"
Git/Ref.hs view
@@ -1,10 +1,12 @@ {- git ref stuff -- - Copyright 2011-2013 Joey Hess <id@joeyh.name>+ - Copyright 2011-2019 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} +{-# LANGUAGE OverloadedStrings #-}+ module Git.Ref where import Common@@ -13,13 +15,14 @@ import Git.Sha import Git.Types -import Data.Char (chr)+import Data.Char (chr, ord)+import qualified Data.ByteString as S headRef :: Ref headRef = Ref "HEAD" headFile :: Repo -> FilePath-headFile r = localGitDir r </> "HEAD"+headFile r = fromRawFilePath (localGitDir r) </> "HEAD" setHeadRef :: Ref -> Repo -> IO () setHeadRef ref r = writeFile (headFile r) ("ref: " ++ fromRef ref)@@ -33,11 +36,18 @@ - Converts such a fully qualified ref into a base ref - (eg: master or origin/master). -} base :: Ref -> Ref-base = Ref . remove "refs/heads/" . remove "refs/remotes/" . fromRef+base = removeBase "refs/heads/" . removeBase "refs/remotes/"++{- Removes a directory such as "refs/heads/master" from a+ - fully qualified ref. Any ref not starting with it is left as-is. -}+removeBase :: String -> Ref -> Ref+removeBase dir (Ref r)+ | prefix `isPrefixOf` r = Ref (drop (length prefix) r)+ | otherwise = Ref r where- remove prefix s- | prefix `isPrefixOf` s = drop (length prefix) s- | otherwise = s+ prefix = case end dir of+ ['/'] -> dir+ _ -> dir ++ "/" {- Given a directory such as "refs/remotes/origin", and a ref such as - refs/heads/master, yields a version of that ref under the directory,@@ -45,14 +55,18 @@ underBase :: String -> Ref -> Ref underBase dir r = Ref $ dir ++ "/" ++ fromRef (base r) +{- Convert a branch such as "master" into a fully qualified ref. -}+branchRef :: Branch -> Ref+branchRef = underBase "refs/heads"+ {- A Ref that can be used to refer to a file in the repository, as staged - in the index. - - Prefixing the file with ./ makes this work even if in a subdirectory - of a repo. -}-fileRef :: FilePath -> Ref-fileRef f = Ref $ ":./" ++ f+fileRef :: RawFilePath -> Ref+fileRef f = Ref $ ":./" ++ fromRawFilePath f {- Converts a Ref to refer to the content of the Ref on a given date. -} dateRef :: Ref -> RefDate -> Ref@@ -60,7 +74,7 @@ {- A Ref that can be used to refer to a file in the repository as it - appears in a given Ref. -}-fileFromRef :: Ref -> FilePath -> Ref+fileFromRef :: Ref -> RawFilePath -> Ref fileFromRef (Ref r) f = let (Ref fr) = fileRef f in Ref (r ++ fr) {- Checks if a ref exists. -}@@ -71,24 +85,29 @@ {- The file used to record a ref. (Git also stores some refs in a - packed-refs file.) -} file :: Ref -> Repo -> FilePath-file ref repo = localGitDir repo </> fromRef ref+file ref repo = fromRawFilePath (localGitDir repo) </> fromRef ref {- Checks if HEAD exists. It generally will, except for in a repository - that was just created. -} headExists :: Repo -> IO Bool headExists repo = do- ls <- lines <$> pipeReadStrict [Param "show-ref", Param "--head"] repo- return $ any (" HEAD" `isSuffixOf`) ls+ ls <- S.split nl <$> pipeReadStrict [Param "show-ref", Param "--head"] repo+ return $ any (" HEAD" `S.isSuffixOf`) ls+ where+ nl = fromIntegral (ord '\n') {- Get the sha of a fully qualified git ref, if it exists. -} sha :: Branch -> Repo -> IO (Maybe Sha) sha branch repo = process <$> showref repo where- showref = pipeReadStrict [Param "show-ref",- Param "--hash", -- get the hash- Param $ fromRef branch]- process [] = Nothing- process s = Just $ Ref $ firstLine s+ showref = pipeReadStrict+ [ Param "show-ref"+ , Param "--hash" -- get the hash+ , Param $ fromRef branch+ ]+ process s+ | S.null s = Nothing+ | otherwise = Just $ Ref $ decodeBS' $ firstLine' s headSha :: Repo -> IO (Maybe Sha) headSha = sha headRef@@ -101,25 +120,47 @@ matchingWithHEAD :: [Ref] -> Repo -> IO [(Sha, Branch)] matchingWithHEAD refs repo = matching' ("--head" : map fromRef refs) repo -{- List of (shas, branches) matching a given ref or refs. -}+{- List of (shas, branches) matching a given ref spec. -} matching' :: [String] -> Repo -> IO [(Sha, Branch)]-matching' ps repo = map gen . lines <$> +matching' ps repo = map gen . lines . decodeBS' <$> pipeReadStrict (Param "show-ref" : map Param ps) repo where gen l = let (r, b) = separate (== ' ') l in (Ref r, Ref b) -{- List of (shas, branches) matching a given ref spec.+{- List of (shas, branches) matching a given ref. - Duplicate shas are filtered out. -} matchingUniq :: [Ref] -> Repo -> IO [(Sha, Branch)] matchingUniq refs repo = nubBy uniqref <$> matching refs repo where uniqref (a, _) (b, _) = a == b -{- Gets the sha of the tree a ref uses. -}+{- List of all refs. -}+list :: Repo -> IO [(Sha, Ref)]+list = matching' []++{- Deletes a ref. This can delete refs that are not branches, + - which git branch --delete refuses to delete. -}+delete :: Sha -> Ref -> Repo -> IO ()+delete oldvalue ref = run+ [ Param "update-ref"+ , Param "-d"+ , Param $ fromRef ref+ , Param $ fromRef oldvalue+ ]++{- Gets the sha of the tree a ref uses. + -+ - The ref may be something like a branch name, and it could contain+ - ":subdir" if a subtree is wanted. -} tree :: Ref -> Repo -> IO (Maybe Sha)-tree ref = extractSha <$$> pipeReadStrict- [ Param "rev-parse", Param (fromRef ref ++ ":") ]+tree (Ref ref) = extractSha . decodeBS <$$> pipeReadStrict+ [ Param "rev-parse", Param "--verify", Param "--quiet", Param ref' ]+ where+ ref' = if ":" `isInfixOf` ref+ then ref+ -- de-reference commit objects to the tree+ else ref ++ ":" {- Checks if a String is a legal git ref name. -
Git/RefLog.hs view
@@ -2,7 +2,7 @@ - - Copyright 2013 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.RefLog where@@ -21,7 +21,7 @@ getMulti bs = get' (map (Param . fromRef) bs) get' :: [CommandParam] -> Repo -> IO [Sha]-get' ps = mapMaybe extractSha . lines <$$> pipeReadStrict ps'+get' ps = mapMaybe extractSha . lines . decodeBS <$$> pipeReadStrict ps' where ps' = catMaybes [ Just $ Param "log"
Git/Remote.hs view
@@ -2,10 +2,11 @@ - - Copyright 2012 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} {-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-} module Git.Remote where @@ -15,11 +16,22 @@ import Data.Char import qualified Data.Map as M+import qualified Data.ByteString as S+import qualified Data.ByteString.Char8 as S8 import Network.URI #ifdef mingw32_HOST_OS import Git.FilePath #endif +{- Is a git config key one that specifies the location of a remote? -}+isRemoteKey :: ConfigKey -> Bool+isRemoteKey (ConfigKey k) = "remote." `S.isPrefixOf` k && ".url" `S.isSuffixOf` k++{- Get a remote's name from the config key that specifies its location. -}+remoteKeyToRemoteName :: ConfigKey -> RemoteName+remoteKeyToRemoteName (ConfigKey k) = decodeBS' $+ S.intercalate "." $ dropFromEnd 1 $ drop 1 $ S8.split '.' k+ {- Construct a legal git remote name out of an arbitrary input string. - - There seems to be no formal definition of this in the git source,@@ -43,6 +55,7 @@ legal c = isAlphaNum c data RemoteLocation = RemoteUrl String | RemotePath FilePath+ deriving (Eq) remoteLocationIsUrl :: RemoteLocation -> Bool remoteLocationIsUrl (RemoteUrl _) = True@@ -67,16 +80,16 @@ -- insteadof config can rewrite remote location calcloc l | null insteadofs = l- | otherwise = replacement ++ drop (length bestvalue) l+ | otherwise = replacement ++ drop (S.length bestvalue) l where- replacement = drop (length prefix) $- take (length bestkey - length suffix) bestkey- (bestkey, bestvalue) = maximumBy longestvalue insteadofs+ replacement = decodeBS' $ S.drop (S.length prefix) $+ S.take (S.length bestkey - S.length suffix) bestkey+ (ConfigKey bestkey, ConfigValue bestvalue) = maximumBy longestvalue insteadofs longestvalue (_, a) (_, b) = compare b a- insteadofs = filterconfig $ \(k, v) -> - prefix `isPrefixOf` k &&- suffix `isSuffixOf` k &&- v `isPrefixOf` l+ insteadofs = filterconfig $ \(ConfigKey k, ConfigValue v) -> + prefix `S.isPrefixOf` k &&+ suffix `S.isSuffixOf` k &&+ v `S.isPrefixOf` encodeBS l filterconfig f = filter f $ concatMap splitconfigs $ M.toList $ fullconfig repo splitconfigs (k, vs) = map (\v -> (k, v)) vs@@ -104,5 +117,5 @@ -- git on Windows will write a path to .git/config with "drive:", -- which is not to be confused with a "host:" dosstyle = hasDrive- dospath = fromInternalGitPath+ dospath = fromRawFilePath . fromInternalGitPath . toRawFilePath #endif
Git/Repair.hs view
@@ -2,7 +2,7 @@ - - Copyright 2013-2014 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.Repair (@@ -11,7 +11,6 @@ removeBadBranches, successfulRepair, cleanCorruptObjects,- retrieveMissingObjects, resetLocalBranches, checkIndex, checkIndexFast,@@ -36,7 +35,7 @@ import qualified Git.RefLog as RefLog import qualified Git.UpdateIndex as UpdateIndex import qualified Git.Branch as Branch-import Utility.Tmp+import Utility.Tmp.Dir import Utility.Rsync import Utility.FileMode import Utility.Tuple@@ -102,10 +101,11 @@ unlessM (boolSystem "git" [Param "init", File tmpdir]) $ error $ "failed to create temp repository in " ++ tmpdir tmpr <- Config.read =<< Construct.fromAbsPath tmpdir- stillmissing <- pullremotes tmpr (remotes r) fetchrefstags missing+ rs <- Construct.fromRemotes r+ stillmissing <- pullremotes tmpr rs fetchrefstags missing if S.null (knownMissing stillmissing) then return stillmissing- else pullremotes tmpr (remotes r) fetchallrefs stillmissing+ else pullremotes tmpr rs fetchallrefs stillmissing where pullremotes tmpr [] fetchrefs stillmissing = case referencerepo of Nothing -> return stillmissing@@ -227,7 +227,7 @@ - Relies on packed refs being exploded before it's called. -} getAllRefs :: Repo -> IO [Ref]-getAllRefs r = getAllRefs' (localGitDir r </> "refs")+getAllRefs r = getAllRefs' (fromRawFilePath (localGitDir r) </> "refs") getAllRefs' :: FilePath -> IO [Ref] getAllRefs' refdir = do@@ -245,13 +245,13 @@ nukeFile f where makeref (sha, ref) = do- let dest = localGitDir r </> fromRef ref+ let dest = fromRawFilePath (localGitDir r) </> fromRef ref createDirectoryIfMissing True (parentDir dest) unlessM (doesFileExist dest) $ writeFile dest (fromRef sha) packedRefsFile :: Repo -> FilePath-packedRefsFile r = localGitDir r </> "packed-refs"+packedRefsFile r = fromRawFilePath (localGitDir r) </> "packed-refs" parsePacked :: String -> Maybe (Sha, Ref) parsePacked l = case words l of@@ -263,7 +263,7 @@ {- git-branch -d cannot be used to remove a branch that is directly - pointing to a corrupt commit. -} nukeBranchRef :: Branch -> Repo -> IO ()-nukeBranchRef b r = nukeFile $ localGitDir r </> fromRef b+nukeBranchRef b r = nukeFile $ fromRawFilePath (localGitDir r) </> fromRef b {- Finds the most recent commit to a branch that does not need any - of the missing objects. If the input branch is good as-is, returns it.@@ -284,7 +284,7 @@ , Param "--format=%H" , Param (fromRef branch) ] r- let branchshas = catMaybes $ map extractSha ls+ let branchshas = catMaybes $ map (extractSha . decodeBL) ls reflogshas <- RefLog.get branch r -- XXX Could try a bit harder here, and look -- for uncorrupted old commits in branches in the@@ -313,7 +313,7 @@ , Param "--format=%H %T" , Param (fromRef commit) ] r- let committrees = map parse ls+ let committrees = map (parse . decodeBL) ls if any isNothing committrees || null committrees then do void cleanup@@ -341,8 +341,8 @@ verifyTree missing treesha r | S.member treesha missing = return False | otherwise = do- (ls, cleanup) <- pipeNullSplit (LsTree.lsTreeParams treesha []) r- let objshas = map (LsTree.sha . LsTree.parseLsTree) ls+ (ls, cleanup) <- pipeNullSplit (LsTree.lsTreeParams LsTree.LsTreeRecursive treesha []) r+ let objshas = mapMaybe (LsTree.sha <$$> eitherToMaybe . LsTree.parseLsTree) ls if any (`S.member` missing) objshas then do void cleanup@@ -370,7 +370,7 @@ length indexcontents `seq` cleanup missingIndex :: Repo -> IO Bool-missingIndex r = not <$> doesFileExist (localGitDir r </> "index")+missingIndex r = not <$> doesFileExist (fromRawFilePath (localGitDir r) </> "index") {- Finds missing and ok files staged in the index. -} partitionIndex :: Repo -> IO ([LsFiles.StagedDetails], [LsFiles.StagedDetails], IO Bool)@@ -394,12 +394,12 @@ UpdateIndex.streamUpdateIndex r =<< (catMaybes <$> mapM reinject good) void cleanup- return $ map fst3 bad+ return $ map (fromRawFilePath . fst3) bad where- reinject (file, Just sha, Just mode) = case toBlobType mode of+ reinject (file, Just sha, Just mode) = case toTreeItemType mode of Nothing -> return Nothing- Just blobtype -> Just <$>- UpdateIndex.stageFile sha blobtype file r+ Just treeitemtype -> Just <$>+ UpdateIndex.stageFile sha treeitemtype (fromRawFilePath file) r reinject _ = return Nothing newtype GoodCommits = GoodCommits (S.Set Sha)@@ -446,7 +446,7 @@ let f = indexFile g void $ tryIO $ allowWrite f where- headfile = localGitDir g </> "HEAD"+ headfile = fromRawFilePath (localGitDir g) </> "HEAD" validhead s = "ref: refs/" `isPrefixOf` s || isJust (extractSha s) {- Put it all together. -}
Git/Sha.hs view
@@ -2,7 +2,7 @@ - - Copyright 2011 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.Sha where
Git/Types.hs view
@@ -1,16 +1,23 @@ {- git data types -- - Copyright 2010-2012 Joey Hess <id@joeyh.name>+ - Copyright 2010-2019 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} +{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+ module Git.Types where import Network.URI+import Data.String+import Data.Default import qualified Data.Map as M+import qualified Data.ByteString as S import System.Posix.Types import Utility.SafeCommand+import Utility.FileSystemEncoding {- Support repositories on local disk, and repositories accessed via an URL. -@@ -23,19 +30,19 @@ - else known about it. -} data RepoLocation- = Local { gitdir :: FilePath, worktree :: Maybe FilePath }- | LocalUnknown FilePath+ = Local { gitdir :: RawFilePath, worktree :: Maybe RawFilePath }+ | LocalUnknown RawFilePath | Url URI | Unknown deriving (Show, Eq, Ord) data Repo = Repo { location :: RepoLocation- , config :: M.Map String String+ , config :: M.Map ConfigKey ConfigValue -- a given git config key can actually have multiple values- , fullconfig :: M.Map String [String]- , remotes :: [Repo]- -- remoteName holds the name used for this repo in remotes+ , fullconfig :: M.Map ConfigKey [ConfigValue]+ -- remoteName holds the name used for this repo in some other+ -- repo's list of remotes, when this repo is such a remote , remoteName :: Maybe RemoteName -- alternate environment to use when running git commands , gitEnv :: Maybe [(String, String)]@@ -44,6 +51,33 @@ , gitGlobalOpts :: [CommandParam] } deriving (Show, Eq, Ord) +newtype ConfigKey = ConfigKey S.ByteString+ deriving (Ord, Eq)++newtype ConfigValue = ConfigValue S.ByteString+ deriving (Ord, Eq, Semigroup, Monoid)++instance Default ConfigValue where+ def = ConfigValue mempty++fromConfigKey :: ConfigKey -> String+fromConfigKey (ConfigKey s) = decodeBS' s++instance Show ConfigKey where+ show = fromConfigKey++fromConfigValue :: ConfigValue -> String+fromConfigValue (ConfigValue s) = decodeBS' s++instance Show ConfigValue where+ show = fromConfigValue++instance IsString ConfigKey where+ fromString = ConfigKey . encodeBS'++instance IsString ConfigValue where+ fromString = ConfigValue . encodeBS'+ type RemoteName = String {- A git ref. Can be a sha1, or a branch or tag name. -}@@ -64,45 +98,48 @@ {- Types of objects that can be stored in git. -} data ObjectType = BlobObject | CommitObject | TreeObject- deriving (Eq) -instance Show ObjectType where- show BlobObject = "blob"- show CommitObject = "commit"- show TreeObject = "tree"--readObjectType :: String -> Maybe ObjectType+readObjectType :: S.ByteString -> Maybe ObjectType readObjectType "blob" = Just BlobObject readObjectType "commit" = Just CommitObject readObjectType "tree" = Just TreeObject readObjectType _ = Nothing -{- Types of blobs. -}-data BlobType = FileBlob | ExecutableBlob | SymlinkBlob- deriving (Eq)+fmtObjectType :: ObjectType -> S.ByteString+fmtObjectType BlobObject = "blob"+fmtObjectType CommitObject = "commit"+fmtObjectType TreeObject = "tree" -{- Git uses magic numbers to denote the type of a blob. -}-instance Show BlobType where- show FileBlob = "100644"- show ExecutableBlob = "100755"- show SymlinkBlob = "120000"+{- Types of items in a tree. -}+data TreeItemType = TreeFile | TreeExecutable | TreeSymlink | TreeSubmodule+ deriving (Eq, Show) -readBlobType :: String -> Maybe BlobType-readBlobType "100644" = Just FileBlob-readBlobType "100755" = Just ExecutableBlob-readBlobType "120000" = Just SymlinkBlob-readBlobType _ = Nothing+{- Git uses magic numbers to denote the type of a tree item. -}+readTreeItemType :: S.ByteString -> Maybe TreeItemType+readTreeItemType "100644" = Just TreeFile+readTreeItemType "100755" = Just TreeExecutable+readTreeItemType "120000" = Just TreeSymlink+readTreeItemType "160000" = Just TreeSubmodule+readTreeItemType _ = Nothing -toBlobType :: FileMode -> Maybe BlobType-toBlobType 0o100644 = Just FileBlob-toBlobType 0o100755 = Just ExecutableBlob-toBlobType 0o120000 = Just SymlinkBlob-toBlobType _ = Nothing+fmtTreeItemType :: TreeItemType -> S.ByteString+fmtTreeItemType TreeFile = "100644"+fmtTreeItemType TreeExecutable = "100755"+fmtTreeItemType TreeSymlink = "120000"+fmtTreeItemType TreeSubmodule = "160000" -fromBlobType :: BlobType -> FileMode-fromBlobType FileBlob = 0o100644-fromBlobType ExecutableBlob = 0o100755-fromBlobType SymlinkBlob = 0o120000+toTreeItemType :: FileMode -> Maybe TreeItemType+toTreeItemType 0o100644 = Just TreeFile+toTreeItemType 0o100755 = Just TreeExecutable+toTreeItemType 0o120000 = Just TreeSymlink+toTreeItemType 0o160000 = Just TreeSubmodule+toTreeItemType _ = Nothing++fromTreeItemType :: TreeItemType -> FileMode+fromTreeItemType TreeFile = 0o100644+fromTreeItemType TreeExecutable = 0o100755+fromTreeItemType TreeSymlink = 0o120000+fromTreeItemType TreeSubmodule = 0o160000 data Commit = Commit { commitTree :: Sha
Git/UpdateIndex.hs view
@@ -1,11 +1,11 @@ {- git-update-index library -- - Copyright 2011-2013 Joey Hess <id@joeyh.name>+ - Copyright 2011-2019 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} -{-# LANGUAGE BangPatterns, CPP #-}+{-# LANGUAGE BangPatterns, OverloadedStrings, CPP #-} module Git.UpdateIndex ( Streamer,@@ -21,6 +21,7 @@ unstageFile, stageSymlink, stageDiffTreeItem,+ refreshIndex, ) where import Common@@ -31,12 +32,14 @@ import Git.Sha import qualified Git.DiffTreeItem as Diff +import qualified Data.ByteString.Lazy as L+ {- Streamers are passed a callback and should feed it lines in the form - read by update-index, and generated by ls-tree. -}-type Streamer = (String -> IO ()) -> IO ()+type Streamer = (L.ByteString -> IO ()) -> IO () {- A streamer with a precalculated value. -}-pureStreamer :: String -> Streamer+pureStreamer :: L.ByteString -> Streamer pureStreamer !s = \streamer -> streamer s {- Streams content into update-index from a list of Streamers. -}@@ -48,8 +51,8 @@ streamUpdateIndex' :: UpdateIndexHandle -> Streamer -> IO () streamUpdateIndex' (UpdateIndexHandle _ h) a = a $ \s -> do- hPutStr h s- hPutStr h "\0"+ L.hPutStr h s+ L.hPutStr h "\0" startUpdateIndex :: Repo -> IO UpdateIndexHandle startUpdateIndex repo = do@@ -83,38 +86,66 @@ {- Generates a line suitable to be fed into update-index, to add - a given file with a given sha. -}-updateIndexLine :: Sha -> BlobType -> TopFilePath -> String-updateIndexLine sha filetype file =- show filetype ++ " blob " ++ fromRef sha ++ "\t" ++ indexPath file+updateIndexLine :: Sha -> TreeItemType -> TopFilePath -> L.ByteString+updateIndexLine sha treeitemtype file = L.fromStrict $+ fmtTreeItemType treeitemtype+ <> " blob "+ <> encodeBS (fromRef sha)+ <> "\t"+ <> indexPath file -stageFile :: Sha -> BlobType -> FilePath -> Repo -> IO Streamer-stageFile sha filetype file repo = do- p <- toTopFilePath file repo- return $ pureStreamer $ updateIndexLine sha filetype p+stageFile :: Sha -> TreeItemType -> FilePath -> Repo -> IO Streamer+stageFile sha treeitemtype file repo = do+ p <- toTopFilePath (toRawFilePath file) repo+ return $ pureStreamer $ updateIndexLine sha treeitemtype p {- A streamer that removes a file from the index. -} unstageFile :: FilePath -> Repo -> IO Streamer unstageFile file repo = do- p <- toTopFilePath file repo+ p <- toTopFilePath (toRawFilePath file) repo return $ unstageFile' p unstageFile' :: TopFilePath -> Streamer-unstageFile' p = pureStreamer $ "0 " ++ fromRef nullSha ++ "\t" ++ indexPath p+unstageFile' p = pureStreamer $ L.fromStrict $+ "0 "+ <> encodeBS' (fromRef nullSha)+ <> "\t"+ <> indexPath p {- A streamer that adds a symlink to the index. -} stageSymlink :: FilePath -> Sha -> Repo -> IO Streamer stageSymlink file sha repo = do !line <- updateIndexLine <$> pure sha- <*> pure SymlinkBlob- <*> toTopFilePath file repo+ <*> pure TreeSymlink+ <*> toTopFilePath (toRawFilePath file) repo return $ pureStreamer line {- A streamer that applies a DiffTreeItem to the index. -} stageDiffTreeItem :: Diff.DiffTreeItem -> Streamer-stageDiffTreeItem d = case toBlobType (Diff.dstmode d) of+stageDiffTreeItem d = case toTreeItemType (Diff.dstmode d) of Nothing -> unstageFile' (Diff.file d) Just t -> pureStreamer $ updateIndexLine (Diff.dstsha d) t (Diff.file d) indexPath :: TopFilePath -> InternalGitPath indexPath = toInternalGitPath . getTopFilePath++{- Refreshes the index, by checking file stat information. -}+refreshIndex :: Repo -> ((FilePath -> IO ()) -> IO ()) -> IO Bool+refreshIndex repo feeder = do+ (Just h, _, _, p) <- createProcess (gitCreateProcess params repo)+ { std_in = CreatePipe }+ feeder $ \f -> do+ hPutStr h f+ hPutStr h "\0"+ hFlush h+ hClose h+ checkSuccessProcess p+ where+ params = + [ Param "update-index"+ , Param "-q"+ , Param "--refresh"+ , Param "-z"+ , Param "--stdin"+ ]
Git/Url.hs view
@@ -2,7 +2,7 @@ - - Copyright 2010, 2011 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} module Git.Url (@@ -11,9 +11,10 @@ port, hostuser, authority,+ path, ) where -import Network.URI hiding (scheme, authority)+import Network.URI hiding (scheme, authority, path) import Common import Git.Types@@ -65,6 +66,11 @@ authpart :: (URIAuth -> a) -> Repo -> Maybe a authpart a Repo { location = Url u } = a <$> uriAuthority u authpart _ repo = notUrl repo++{- Path part of an URL repo. -}+path :: Repo -> FilePath+path Repo { location = Url u } = uriPath u+path repo = notUrl repo notUrl :: Repo -> a notUrl repo = error $
Git/Version.hs view
@@ -2,7 +2,7 @@ - - Copyright 2011, 2013 Joey Hess <id@joeyh.name> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} {-# OPTIONS_GHC -fno-warn-tabs #-}
Utility/Applicative.hs view
@@ -5,7 +5,11 @@ - License: BSD-2-clause -} -module Utility.Applicative where+{-# OPTIONS_GHC -fno-warn-tabs #-}++module Utility.Applicative (+ (<$$>),+) where {- Like <$> , but supports one level of currying. -
+ Utility/Attoparsec.hs view
@@ -0,0 +1,21 @@+{- attoparsec utility functions+ -+ - Copyright 2019 Joey Hess <id@joeyh.name>+ - Copyright 2007-2015 Bryan O'Sullivan+ -+ - License: BSD-3-clause+ -}++module Utility.Attoparsec where++import qualified Data.Attoparsec.ByteString as A+import qualified Data.ByteString as B++-- | Parse and decode an unsigned octal number.+--+-- This parser does not accept a leading @\"0o\"@ string.+octal :: Integral a => A.Parser a+octal = B.foldl' step 0 `fmap` A.takeWhile1 isOctDigit+ where+ isOctDigit w = w >= 48 && w <= 55+ step a w = a * 8 + fromIntegral (w - 48)
Utility/Batch.hs view
@@ -7,11 +7,18 @@ {-# LANGUAGE CPP #-} -module Utility.Batch where+module Utility.Batch (+ batch,+ BatchCommandMaker,+ getBatchCommandMaker,+ toBatchCommand,+ batchCommand,+ batchCommandEnv,+) where import Common -#if defined(linux_HOST_OS) || defined(__ANDROID__)+#if defined(linux_HOST_OS) import Control.Concurrent.Async import System.Posix.Process #endif@@ -29,7 +36,7 @@ - systems, the action is simply ran. -} batch :: IO a -> IO a-#if defined(linux_HOST_OS) || defined(__ANDROID__)+#if defined(linux_HOST_OS) batch a = wait =<< batchthread where batchthread = asyncBound $ do@@ -51,11 +58,7 @@ #ifndef mingw32_HOST_OS nicers <- filterM (inPath . fst) [ ("nice", [])-#ifndef __ANDROID__- -- Android's ionice does not allow specifying a command,- -- so don't use it. , ("ionice", ["-c3"])-#endif , ("nocache", []) ] return $ \(command, params) ->
Utility/Data.hs view
@@ -7,7 +7,10 @@ {-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.Data where+module Utility.Data (+ firstJust,+ eitherToMaybe,+) where {- First item in the list that is not Nothing. -} firstJust :: Eq a => [Maybe a] -> Maybe a
Utility/Directory.hs view
@@ -16,22 +16,18 @@ import System.IO.Error import Control.Monad import System.FilePath+import System.PosixCompat.Files import Control.Applicative-import Control.Concurrent import System.IO.Unsafe (unsafeInterleaveIO) import Data.Maybe import Prelude -#ifdef mingw32_HOST_OS-import qualified System.Win32 as Win32-#else-import qualified System.Posix as Posix+#ifndef mingw32_HOST_OS import Utility.SafeCommand import Control.Monad.IfElse #endif import Utility.SystemDirectory-import Utility.PosixFiles import Utility.Tmp import Utility.Exception import Utility.Monad@@ -158,90 +154,3 @@ #else go = removeFile file #endif--#ifndef mingw32_HOST_OS-data DirectoryHandle = DirectoryHandle IsOpen Posix.DirStream-#else-data DirectoryHandle = DirectoryHandle IsOpen Win32.HANDLE Win32.FindData (MVar ())-#endif--type IsOpen = MVar () -- full when the handle is open--openDirectory :: FilePath -> IO DirectoryHandle-openDirectory path = do-#ifndef mingw32_HOST_OS- dirp <- Posix.openDirStream path- isopen <- newMVar ()- return (DirectoryHandle isopen dirp)-#else- (h, fdat) <- Win32.findFirstFile (path </> "*")- -- Indicate that the fdat contains a filename that readDirectory- -- has not yet returned, by making the MVar be full.- -- (There's always at least a "." entry.)- alreadyhave <- newMVar ()- isopen <- newMVar ()- return (DirectoryHandle isopen h fdat alreadyhave)-#endif--closeDirectory :: DirectoryHandle -> IO ()-#ifndef mingw32_HOST_OS-closeDirectory (DirectoryHandle isopen dirp) =- whenOpen isopen $- Posix.closeDirStream dirp-#else-closeDirectory (DirectoryHandle isopen h _ alreadyhave) =- whenOpen isopen $ do- _ <- tryTakeMVar alreadyhave- Win32.findClose h-#endif- where- whenOpen :: IsOpen -> IO () -> IO ()- whenOpen mv f = do- v <- tryTakeMVar mv- when (isJust v) f--{- |Reads the next entry from the handle. Once the end of the directory-is reached, returns Nothing and automatically closes the handle.--}-readDirectory :: DirectoryHandle -> IO (Maybe FilePath)-#ifndef mingw32_HOST_OS-readDirectory hdl@(DirectoryHandle _ dirp) = do- e <- Posix.readDirStream dirp- if null e- then do- closeDirectory hdl- return Nothing- else return (Just e)-#else-readDirectory hdl@(DirectoryHandle _ h fdat mv) = do- -- If the MVar is full, then the filename in fdat has- -- not yet been returned. Otherwise, need to find the next- -- file.- r <- tryTakeMVar mv- case r of- Just () -> getfn- Nothing -> do- more <- Win32.findNextFile h fdat- if more- then getfn- else do- closeDirectory hdl- return Nothing- where- getfn = do- filename <- Win32.getFindDataFileName fdat- return (Just filename)-#endif---- True only when directory exists and contains nothing.--- Throws exception if directory does not exist.-isDirectoryEmpty :: FilePath -> IO Bool-isDirectoryEmpty d = bracket (openDirectory d) closeDirectory check- where- check h = do- v <- readDirectory h- case v of- Nothing -> return True- Just f- | not (dirCruft f) -> return False- | otherwise -> check h
Utility/DottedVersion.hs view
@@ -7,7 +7,11 @@ {-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.DottedVersion where+module Utility.DottedVersion (+ DottedVersion,+ fromDottedVersion,+ normalize,+) where import Common @@ -18,7 +22,10 @@ compare (DottedVersion _ x) (DottedVersion _ y) = compare x y instance Show DottedVersion where- show (DottedVersion s _) = s+ show = fromDottedVersion++fromDottedVersion :: DottedVersion -> String+fromDottedVersion (DottedVersion s _) = s {- To compare dotted versions like 1.7.7 and 1.8, they are normalized to - a somewhat arbitrary integer representation. -}
Utility/Env.hs view
@@ -8,7 +8,14 @@ {-# LANGUAGE CPP #-} {-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.Env where+module Utility.Env (+ getEnv,+ getEnvDefault,+ getEnvironment,+ addEntry,+ addEntries,+ delEntry,+) where #ifdef mingw32_HOST_OS import Utility.Exception@@ -16,7 +23,6 @@ import Data.Maybe import Prelude import qualified System.Environment as E-import qualified System.SetEnv #else import qualified System.Posix.Env as PE #endif@@ -40,29 +46,6 @@ getEnvironment = PE.getEnvironment #else getEnvironment = E.getEnvironment-#endif--{- Sets an environment variable. To overwrite an existing variable,- - overwrite must be True.- -- - On Windows, setting a variable to "" unsets it. -}-setEnv :: String -> String -> Bool -> IO ()-#ifndef mingw32_HOST_OS-setEnv var val overwrite = PE.setEnv var val overwrite-#else-setEnv var val True = System.SetEnv.setEnv var val-setEnv var val False = do- r <- getEnv var- case r of- Nothing -> setEnv var val True- Just _ -> return ()-#endif--unsetEnv :: String -> IO ()-#ifndef mingw32_HOST_OS-unsetEnv = PE.unsetEnv-#else-unsetEnv = System.SetEnv.unsetEnv #endif {- Adds the environment variable to the input environment. If already
+ Utility/Env/Basic.hs view
@@ -0,0 +1,25 @@+{- portable environment variables, without any dependencies+ -+ - Copyright 2013 Joey Hess <id@joeyh.name>+ -+ - License: BSD-2-clause+ -}++{-# OPTIONS_GHC -fno-warn-tabs #-}++module Utility.Env.Basic (+ getEnv,+ getEnvDefault,+) where++import Utility.Exception+import Control.Applicative+import Data.Maybe+import Prelude+import qualified System.Environment as E++getEnv :: String -> IO (Maybe String)+getEnv = catchMaybeIO . E.getEnv++getEnvDefault :: String -> String -> IO String+getEnvDefault var fallback = fromMaybe fallback <$> getEnv var
+ Utility/Env/Set.hs view
@@ -0,0 +1,43 @@+{- portable environment variables+ -+ - Copyright 2013 Joey Hess <id@joeyh.name>+ -+ - License: BSD-2-clause+ -}++{-# LANGUAGE CPP #-}++module Utility.Env.Set (+ setEnv,+ unsetEnv,+) where++#ifdef mingw32_HOST_OS+import qualified System.SetEnv+import Utility.Env+#else+import qualified System.Posix.Env as PE+#endif++{- Sets an environment variable. To overwrite an existing variable,+ - overwrite must be True.+ -+ - On Windows, setting a variable to "" unsets it. -}+setEnv :: String -> String -> Bool -> IO ()+#ifndef mingw32_HOST_OS+setEnv var val overwrite = PE.setEnv var val overwrite+#else+setEnv var val True = System.SetEnv.setEnv var val+setEnv var val False = do+ r <- getEnv var+ case r of+ Nothing -> setEnv var val True+ Just _ -> return ()+#endif++unsetEnv :: String -> IO ()+#ifndef mingw32_HOST_OS+unsetEnv = PE.unsetEnv+#else+unsetEnv = System.SetEnv.unsetEnv+#endif
Utility/Exception.hs view
@@ -5,7 +5,7 @@ - License: BSD-2-clause -} -{-# LANGUAGE CPP, ScopedTypeVariables #-}+{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -fno-warn-tabs #-} module Utility.Exception (@@ -29,11 +29,7 @@ import Control.Monad.Catch as X hiding (Handler) import qualified Control.Monad.Catch as M import Control.Exception (IOException, AsyncException)-#ifdef MIN_VERSION_GLASGOW_HASKELL-#if MIN_VERSION_GLASGOW_HASKELL(7,10,0,0) import Control.Exception (SomeAsyncException)-#endif-#endif import Control.Monad import Control.Monad.IO.Class (liftIO, MonadIO) import System.IO.Error (isDoesNotExistError, ioeGetErrorType)@@ -46,15 +42,7 @@ - where there's a problem that the user is excpected to see in some - circumstances. -} giveup :: [Char] -> a-#ifdef MIN_VERSION_base-#if MIN_VERSION_base(4,9,0) giveup = errorWithoutStackTrace-#else-giveup = error-#endif-#else-giveup = error-#endif {- Catches IO errors and returns a Bool -} catchBoolIO :: MonadCatch m => m Bool -> m Bool@@ -95,11 +83,7 @@ catchNonAsync :: MonadCatch m => m a -> (SomeException -> m a) -> m a catchNonAsync a onerr = a `catches` [ M.Handler (\ (e :: AsyncException) -> throwM e)-#ifdef MIN_VERSION_GLASGOW_HASKELL-#if MIN_VERSION_GLASGOW_HASKELL(7,10,0,0) , M.Handler (\ (e :: SomeAsyncException) -> throwM e)-#endif-#endif , M.Handler (\ (e :: SomeException) -> onerr e) ]
Utility/FileMode.hs view
@@ -15,9 +15,9 @@ import System.IO import Control.Monad import System.PosixCompat.Types-import Utility.PosixFiles+import System.PosixCompat.Files #ifndef mingw32_HOST_OS-import System.Posix.Files+import System.Posix.Files (symbolicLinkMode) import Control.Monad.IO.Class (liftIO) #endif import Control.Monad.IO.Class (MonadIO)@@ -69,6 +69,7 @@ otherGroupModes = [ groupReadMode, otherReadMode , groupWriteMode, otherWriteMode+ , groupExecuteMode, otherExecuteMode ] {- Removes the write bits from a file. -}
Utility/FileSize.hs view
@@ -4,8 +4,13 @@ -} {-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.FileSize where+module Utility.FileSize (+ FileSize,+ getFileSize,+ getFileSize',+) where import System.PosixCompat.Files #ifdef mingw32_HOST_OS@@ -28,7 +33,10 @@ getFileSize f = bracket (openFile f ReadMode) hClose hFileSize #endif -{- Gets the size of the file, when its FileStatus is already known. -}+{- Gets the size of the file, when its FileStatus is already known.+ -+ - On windows, uses getFileSize. Otherwise, the FileStatus contains the+ - size, so this does not do any work. -} getFileSize' :: FilePath -> FileStatus -> IO FileSize #ifndef mingw32_HOST_OS getFileSize' _ s = return $ fromIntegral $ fileSize s
Utility/FileSystemEncoding.hs view
@@ -12,12 +12,17 @@ useFileSystemEncoding, fileEncoding, withFilePath,+ RawFilePath,+ fromRawFilePath,+ toRawFilePath,+ decodeBL,+ encodeBL, decodeBS, encodeBS,- decodeW8,- encodeW8,- encodeW8NUL,- decodeW8NUL,+ decodeBL',+ encodeBL',+ decodeBS',+ encodeBS', truncateFilePath, s2w8, w82s,@@ -32,8 +37,10 @@ import System.IO.Unsafe import Data.Word import Data.List+import qualified Data.ByteString as S import qualified Data.ByteString.Lazy as L #ifdef mingw32_HOST_OS+import qualified Data.ByteString.UTF8 as S8 import qualified Data.ByteString.Lazy.UTF8 as L8 #endif @@ -103,31 +110,91 @@ `catchNonAsync` (\_ -> return fp) {- Decodes a ByteString into a FilePath, applying the filesystem encoding. -}-decodeBS :: L.ByteString -> FilePath+decodeBL :: L.ByteString -> FilePath #ifndef mingw32_HOST_OS-decodeBS = encodeW8NUL . L.unpack+decodeBL = encodeW8NUL . L.unpack #else {- On Windows, we assume that the ByteString is utf-8, since Windows - only uses unicode for filenames. -}-decodeBS = L8.toString+decodeBL = L8.toString #endif {- Encodes a FilePath into a ByteString, applying the filesystem encoding. -}-encodeBS :: FilePath -> L.ByteString+encodeBL :: FilePath -> L.ByteString #ifndef mingw32_HOST_OS-encodeBS = L.pack . decodeW8NUL+encodeBL = L.pack . decodeW8NUL #else-encodeBS = L8.fromString+encodeBL = L8.fromString #endif +decodeBS :: S.ByteString -> FilePath+#ifndef mingw32_HOST_OS+decodeBS = encodeW8NUL . S.unpack+#else+decodeBS = S8.toString+#endif++encodeBS :: FilePath -> S.ByteString+#ifndef mingw32_HOST_OS+encodeBS = S.pack . decodeW8NUL+#else+encodeBS = S8.fromString+#endif++{- Faster version that assumes the string does not contain NUL;+ - if it does it will be truncated before the NUL. -}+decodeBS' :: S.ByteString -> FilePath+#ifndef mingw32_HOST_OS+decodeBS' = encodeW8 . S.unpack+#else+decodeBS' = S8.toString+#endif++encodeBS' :: FilePath -> S.ByteString+#ifndef mingw32_HOST_OS+encodeBS' = S.pack . decodeW8+#else+encodeBS' = S8.fromString+#endif++decodeBL' :: L.ByteString -> FilePath+#ifndef mingw32_HOST_OS+decodeBL' = encodeW8 . L.unpack+#else+decodeBL' = L8.toString+#endif++encodeBL' :: FilePath -> L.ByteString+#ifndef mingw32_HOST_OS+encodeBL' = L.pack . decodeW8+#else+encodeBL' = L8.fromString+#endif++{- Recent versions of the unix package have this alias; defined here+ - for backwards compatibility. -}+type RawFilePath = S.ByteString++{- Note that the RawFilePath is assumed to never contain NUL,+ - since filename's don't. This should only be used with actual+ - RawFilePaths not arbitrary ByteString that may contain NUL. -}+fromRawFilePath :: RawFilePath -> FilePath+fromRawFilePath = decodeBS'++{- Note that the FilePath is assumed to never contain NUL,+ - since filename's don't. This should only be used with actual FilePaths+ - not arbitrary String that may contain NUL. -}+toRawFilePath :: FilePath -> RawFilePath+toRawFilePath = encodeBS'+ {- Converts a [Word8] to a FilePath, encoding using the filesystem encoding. -- - w82c produces a String, which may contain Chars that are invalid+ - w82s produces a String, which may contain Chars that are invalid - unicode. From there, this is really a simple matter of applying the - file system encoding, only complicated by GHC's interface to doing so. - - Note that the encoding stops at any NUL in the input. FilePaths- - do not normally contain embedded NUL, but Haskell Strings may.+ - cannot contain embedded NUL, but Haskell Strings may. -} {-# NOINLINE encodeW8 #-} encodeW8 :: [Word8] -> FilePath@@ -135,8 +202,6 @@ enc <- Encoding.getFileSystemEncoding GHC.withCString Encoding.char8 (w82s w8) $ GHC.peekCString enc -{- Useful when you want the actual number of bytes that will be used to- - represent the FilePath on disk. -} decodeW8 :: FilePath -> [Word8] decodeW8 = s2w8 . _encodeFilePath
Utility/Format.hs view
@@ -15,7 +15,7 @@ ) where import Text.Printf (printf)-import Data.Char (isAlphaNum, isOctDigit, isHexDigit, isSpace, chr, ord)+import Data.Char (isAlphaNum, isOctDigit, isHexDigit, isSpace, chr, ord, isAscii) import Data.Maybe (fromMaybe) import Data.Word (Word8) import Data.List (isPrefixOf)@@ -176,12 +176,12 @@ {- For quickcheck. - - Encoding and then decoding roundtrips only when- - the string does not contain high unicode, because eg, - - both "\12345" and "\227\128\185" are encoded to "\343\200\271".+ - the string is ascii because eg, both "\12345" and+ - "\227\128\185" are encoded to "\343\200\271". -- - This property papers over the problem, by only testing chars < 256.+ - This property papers over the problem, by only testing ascii. -} prop_encode_c_decode_c_roundtrip :: String -> Bool prop_encode_c_decode_c_roundtrip s = s' == decode_c (encode_c s') where- s' = filter (\c -> ord c < 256) s+ s' = filter isAscii s
Utility/HumanNumber.hs view
@@ -5,7 +5,7 @@ - License: BSD-2-clause -} -module Utility.HumanNumber where+module Utility.HumanNumber (showImprecise) where {- Displays a fractional value as a string with a limited number - of decimal digits. -}
Utility/HumanTime.hs view
@@ -60,15 +60,17 @@ fromDuration :: Duration -> String fromDuration Duration { durationSeconds = d } | d == 0 = "0s"- | otherwise = concatMap showunit $ go [] units d+ | otherwise = concatMap showunit $ take 2 $ go [] units d where- showunit (u, n)- | n > 0 = show n ++ [u]- | otherwise = ""+ showunit (u, n) = show n ++ [u] go c [] _ = reverse c go c ((u, n):us) v = let (q,r) = v `quotRem` n- in go ((u, q):c) us r+ in if q > 0+ then go ((u, q):c) us r+ else if null c+ then go c us r+ else reverse c units :: [(Char, Integer)] units =
Utility/Metered.hs view
@@ -1,16 +1,48 @@ {- Metered IO and actions -- - Copyright 2012-2016 Joey Hess <id@joeyh.name>+ - Copyright 2012-2018 Joey Hess <id@joeyh.name> - - License: BSD-2-clause -} {-# LANGUAGE TypeSynonymInstances, BangPatterns #-} -module Utility.Metered where+module Utility.Metered (+ MeterUpdate,+ nullMeterUpdate,+ combineMeterUpdate,+ BytesProcessed(..),+ toBytesProcessed,+ fromBytesProcessed,+ addBytesProcessed,+ zeroBytesProcessed,+ withMeteredFile,+ meteredWrite,+ meteredWrite',+ meteredWriteFile,+ offsetMeterUpdate,+ hGetContentsMetered,+ hGetMetered,+ defaultChunkSize,+ watchFileSize,+ OutputHandler(..),+ ProgressParser,+ commandMeter,+ commandMeter',+ demeterCommand,+ demeterCommandEnv,+ avoidProgress,+ rateLimitMeterUpdate,+ Meter,+ mkMeter,+ setMeterTotalSize,+ updateMeter,+ displayMeterHandle,+ clearMeterHandle,+ bandwidthMeter,+) where import Common-import Utility.FileSystemEncoding import Utility.Percentage import Utility.DataUnits import Utility.HumanTime@@ -81,11 +113,6 @@ withMeteredFile f meterupdate a = withBinaryFile f ReadMode $ \h -> hGetContentsMetered h meterupdate >>= a -{- Sends the content of a file to a Handle, updating the meter as it's- - written. -}-streamMeteredFile :: FilePath -> MeterUpdate -> Handle -> IO ()-streamMeteredFile f meterupdate h = withMeteredFile f meterupdate $ L.hPut h- {- Writes a ByteString to a Handle, updating a meter as it's written. -} meteredWrite :: MeterUpdate -> Handle -> L.ByteString -> IO () meteredWrite meterupdate h = void . meteredWrite' meterupdate h@@ -211,7 +238,14 @@ - to update a meter. -} commandMeter :: ProgressParser -> OutputHandler -> MeterUpdate -> FilePath -> [CommandParam] -> IO Bool-commandMeter progressparser oh meterupdate cmd params = +commandMeter progressparser oh meterupdate cmd params = do+ ret <- commandMeter' progressparser oh meterupdate cmd params+ return $ case ret of+ Just ExitSuccess -> True+ _ -> False++commandMeter' :: ProgressParser -> OutputHandler -> MeterUpdate -> FilePath -> [CommandParam] -> IO (Maybe ExitCode)+commandMeter' progressparser oh meterupdate cmd params = outputFilter cmd params Nothing (feedprogress zeroBytesProcessed []) handlestderr@@ -224,7 +258,7 @@ unless (quietMode oh) $ do S.hPut stdout b hFlush stdout- let s = encodeW8 (S.unpack b)+ let s = decodeBS b let (mbytes, buf') = progressparser (buf++s) case mbytes of Nothing -> feedprogress prev buf' h@@ -246,9 +280,13 @@ demeterCommand oh cmd params = demeterCommandEnv oh cmd params Nothing demeterCommandEnv :: OutputHandler -> FilePath -> [CommandParam] -> Maybe [(String, String)] -> IO Bool-demeterCommandEnv oh cmd params environ = outputFilter cmd params environ- (\outh -> avoidProgress True outh stdouthandler)- (\errh -> avoidProgress True errh $ stderrHandler oh)+demeterCommandEnv oh cmd params environ = do+ ret <- outputFilter cmd params environ+ (\outh -> avoidProgress True outh stdouthandler)+ (\errh -> avoidProgress True errh $ stderrHandler oh)+ return $ case ret of+ Just ExitSuccess -> True+ _ -> False where stdouthandler l = unless (quietMode oh) $@@ -271,16 +309,15 @@ -> Maybe [(String, String)] -> (Handle -> IO ()) -> (Handle -> IO ())- -> IO Bool-outputFilter cmd params environ outfilter errfilter = catchBoolIO $ do+ -> IO (Maybe ExitCode)+outputFilter cmd params environ outfilter errfilter = catchMaybeIO $ do (_, Just outh, Just errh, pid) <- createProcess p { std_out = CreatePipe , std_err = CreatePipe } void $ async $ tryIO (outfilter outh) >> hClose outh void $ async $ tryIO (errfilter errh) >> hClose errh- ret <- checkSuccessProcess pid- return ret+ waitForProcess pid where p = (proc cmd (toCommand params)) { env = environ }@@ -288,14 +325,14 @@ -- | Limit a meter to only update once per unit of time. -- -- It's nice to display the final update to 100%, even if it comes soon--- after a previous update. To make that happen, a total size has to be--- provided.-rateLimitMeterUpdate :: NominalDiffTime -> Maybe Integer -> MeterUpdate -> IO MeterUpdate-rateLimitMeterUpdate delta totalsize meterupdate = do+-- after a previous update. To make that happen, the Meter has to know+-- its total size.+rateLimitMeterUpdate :: NominalDiffTime -> Meter -> MeterUpdate -> IO MeterUpdate+rateLimitMeterUpdate delta (Meter totalsizev _ _ _) meterupdate = do lastupdate <- newMVar (toEnum 0 :: POSIXTime) return $ mu lastupdate where- mu lastupdate n@(BytesProcessed i) = case totalsize of+ mu lastupdate n@(BytesProcessed i) = readMVar totalsizev >>= \case Just t | i >= t -> meterupdate n _ -> do now <- getPOSIXTime@@ -306,35 +343,38 @@ meterupdate n else putMVar lastupdate prev -data Meter = Meter (Maybe Integer) (MVar MeterState) (MVar String) RenderMeter DisplayMeter+data Meter = Meter (MVar (Maybe Integer)) (MVar MeterState) (MVar String) DisplayMeter type MeterState = (BytesProcessed, POSIXTime) -type DisplayMeter = MVar String -> String -> IO ()+type DisplayMeter = MVar String -> Maybe Integer -> (BytesProcessed, POSIXTime) -> (BytesProcessed, POSIXTime) -> IO () type RenderMeter = Maybe Integer -> (BytesProcessed, POSIXTime) -> (BytesProcessed, POSIXTime) -> String -- | Make a meter. Pass the total size, if it's known.-mkMeter :: Maybe Integer -> RenderMeter -> DisplayMeter -> IO Meter-mkMeter totalsize rendermeter displaymeter = Meter- <$> pure totalsize+mkMeter :: Maybe Integer -> DisplayMeter -> IO Meter+mkMeter totalsize displaymeter = Meter+ <$> newMVar totalsize <*> ((\t -> newMVar (zeroBytesProcessed, t)) =<< getPOSIXTime) <*> newMVar ""- <*> pure rendermeter <*> pure displaymeter +setMeterTotalSize :: Meter -> Integer -> IO ()+setMeterTotalSize (Meter totalsizev _ _ _) = void . swapMVar totalsizev . Just+ -- | Updates the meter, displaying it if necessary.-updateMeter :: Meter -> BytesProcessed -> IO ()-updateMeter (Meter totalsize sv bv rendermeter displaymeter) new = do+updateMeter :: Meter -> MeterUpdate+updateMeter (Meter totalsizev sv bv displaymeter) new = do now <- getPOSIXTime (old, before) <- swapMVar sv (new, now)- when (old /= new) $- displaymeter bv $ - rendermeter totalsize (old, before) (new, now)+ when (old /= new) $ do+ totalsize <- readMVar totalsizev+ displaymeter bv totalsize (old, before) (new, now) -- | Display meter to a Handle.-displayMeterHandle :: Handle -> DisplayMeter-displayMeterHandle h v s = do+displayMeterHandle :: Handle -> RenderMeter -> DisplayMeter+displayMeterHandle h rendermeter v msize old new = do+ let s = rendermeter msize old new olds <- swapMVar v s -- Avoid writing when the rendered meter has not changed. when (olds /= s) $ do@@ -344,29 +384,32 @@ -- | Clear meter displayed by displayMeterHandle. clearMeterHandle :: Meter -> Handle -> IO ()-clearMeterHandle (Meter _ _ v _ _) h = do+clearMeterHandle (Meter _ _ v _) h = do olds <- readMVar v hPutStr h $ '\r' : replicate (length olds) ' ' ++ "\r" hFlush h -- | Display meter in the form:--- 10% 300 KiB/s 16m40s+-- 10% 1.3MiB 300 KiB/s 16m40s -- or when total size is not known:--- 1.3 MiB 300 KiB/s+-- 1.3 MiB 300 KiB/s bandwidthMeter :: RenderMeter bandwidthMeter mtotalsize (BytesProcessed old, before) (BytesProcessed new, now) = unwords $ catMaybes- [ Just percentoramount- -- Pad enough for max width: "xxxx.xx KiB xxxx KiB/s"- , Just $ replicate (23 - length percentoramount - length rate) ' '+ [ Just percentamount+ -- Pad enough for max width: "100% xxxx.xx KiB xxxx KiB/s"+ , Just $ replicate (29 - length percentamount - length rate) ' ' , Just rate , estimatedcompletion ] where- percentoramount = case mtotalsize of- Just totalsize -> showPercentage 0 $- percentage totalsize (min new totalsize)- Nothing -> roughSize' memoryUnits True 2 new+ amount = roughSize' memoryUnits True 2 new+ percentamount = case mtotalsize of+ Just totalsize ->+ let p = showPercentage 0 $+ percentage totalsize (min new totalsize)+ in p ++ replicate (6 - length p) ' ' ++ amount+ Nothing -> amount rate = roughSize' memoryUnits True 0 bytespersecond ++ "/s" bytespersecond | duration == 0 = fromIntegral transferred@@ -377,5 +420,5 @@ Just totalsize | bytespersecond > 0 -> Just $ fromDuration $ Duration $- totalsize `div` bytespersecond+ (totalsize - new) `div` bytespersecond _ -> Nothing
Utility/Misc.hs view
@@ -5,22 +5,31 @@ - License: BSD-2-clause -} -{-# LANGUAGE CPP #-} {-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.Misc where+module Utility.Misc (+ hGetContentsStrict,+ readFileStrict,+ separate,+ firstLine,+ firstLine',+ segment,+ segmentDelim,+ massReplace,+ hGetSomeString,+ exitBool, + prop_segment_regressionTest,+) where+ import System.IO import Control.Monad import Foreign import Data.Char import Data.List import System.Exit-#ifndef mingw32_HOST_OS-import System.Posix.Process (getAnyProcessStatus)-import Utility.Exception-#endif import Control.Applicative+import qualified Data.ByteString as S import Prelude {- A version of hgetContents that is not lazy. Ensures file is @@ -49,6 +58,11 @@ firstLine :: String -> String firstLine = takeWhile (/= '\n') +firstLine' :: S.ByteString -> S.ByteString+firstLine' = S.takeWhile (/= nl)+ where+ nl = fromIntegral (ord '\n')+ {- Splits a list into segments that are delimited by items matching - a predicate. (The delimiters are not included in the segments.) - Segments may be empty. -}@@ -111,22 +125,6 @@ where peekbytes :: Int -> Ptr Word8 -> IO [Word8] peekbytes len buf = mapM (peekElemOff buf) [0..pred len]--{- Reaps any zombie git processes. - -- - Warning: Not thread safe. Anything that was expecting to wait- - on a process and get back an exit status is going to be confused- - if this reap gets there first. -}-reapZombies :: IO ()-#ifndef mingw32_HOST_OS-reapZombies =- -- throws an exception when there are no child processes- catchDefaultIO Nothing (getAnyProcessStatus False True)- >>= maybe (return ()) (const reapZombies)--#else-reapZombies = return ()-#endif exitBool :: Bool -> IO a exitBool False = exitFailure
Utility/Monad.hs view
@@ -7,7 +7,19 @@ {-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.Monad where+module Utility.Monad (+ firstM,+ getM,+ anyM,+ allM,+ untilTrue,+ ifM,+ (<||>),+ (<&&>),+ observe,+ after,+ noop,+) where import Data.Maybe import Control.Monad
Utility/PartialPrelude.hs view
@@ -7,7 +7,18 @@ {-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.PartialPrelude where+module Utility.PartialPrelude (+ Utility.PartialPrelude.read,+ Utility.PartialPrelude.head,+ Utility.PartialPrelude.tail,+ Utility.PartialPrelude.init,+ Utility.PartialPrelude.last,+ Utility.PartialPrelude.readish,+ Utility.PartialPrelude.headMaybe,+ Utility.PartialPrelude.lastMaybe,+ Utility.PartialPrelude.beginning,+ Utility.PartialPrelude.end,+) where import qualified Data.Maybe @@ -38,11 +49,9 @@ {- Attempts to read a value from a String. -- - Ignores leading/trailing whitespace, and throws away any trailing- - text after the part that can be read.- -- - readMaybe is available in Text.Read in new versions of GHC,- - but that one requires the entire string to be consumed.+ - Unlike Text.Read.readMaybe, this ignores some trailing text+ - after the part that can be read. However, if the trailing text looks+ - like another readable value, it fails. -} readish :: Read a => String -> Maybe a readish s = case reads s of
Utility/Path.hs view
@@ -5,11 +5,33 @@ - License: BSD-2-clause -} -{-# LANGUAGE PackageImports, CPP #-}+{-# LANGUAGE CPP #-} {-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.Path where+module Utility.Path (+ simplifyPath,+ absPathFrom,+ parentDir,+ upFrom,+ dirContains,+ absPath,+ relPathCwdToFile,+ relPathDirToFile,+ relPathDirToFileAbs,+ segmentPaths,+ runSegmentPaths,+ relHome,+ inPath,+ searchPath,+ dotfile,+ sanitizeFilePath,+ splitShortExtensions, + prop_upFrom_basics,+ prop_relPathDirToFile_basics,+ prop_relPathDirToFile_regressionTest,+) where+ import System.FilePath import Data.List import Data.Maybe@@ -17,17 +39,11 @@ import Control.Applicative import Prelude -#ifdef mingw32_HOST_OS-import qualified System.FilePath.Posix as Posix-#else-import System.Posix.Files-import Utility.Exception-#endif- import Utility.Monad import Utility.UserInfo import Utility.Directory import Utility.Split+import Utility.FileSystemEncoding {- Simplifies a path, removing any "." component, collapsing "dir/..", - and removing the trailing path separator.@@ -97,7 +113,10 @@ - are all equivilant. -} dirContains :: FilePath -> FilePath -> Bool-dirContains a b = a == b || a' == b' || (addTrailingPathSeparator a') `isPrefixOf` b'+dirContains a b = a == b+ || a' == b'+ || (addTrailingPathSeparator a') `isPrefixOf` b'+ || a' == "." && normalise ("." </> b') == b' where a' = norm a b' = norm b@@ -136,17 +155,22 @@ -} relPathDirToFileAbs :: FilePath -> FilePath -> FilePath relPathDirToFileAbs from to- | takeDrive from /= takeDrive to = to+#ifdef mingw32_HOST_OS+ | normdrive from /= normdrive to = to+#endif | otherwise = joinPath $ dotdots ++ uncommon where pfrom = sp from pto = sp to- sp = map dropTrailingPathSeparator . splitPath+ sp = map dropTrailingPathSeparator . splitPath . dropDrive common = map fst $ takeWhile same $ zip pfrom pto same (c,d) = c == d uncommon = drop numcommon pto dotdots = replicate (length pfrom - numcommon) ".." numcommon = length common+#ifdef mingw32_HOST_OS+ normdrive = map toLower . takeWhile (/= ':') . takeDrive+#endif prop_relPathDirToFile_basics :: FilePath -> FilePath -> Bool prop_relPathDirToFile_basics from to@@ -180,20 +204,21 @@ - we stop preserving ordering at that point. Presumably a user passing - that many paths in doesn't care too much about order of the later ones. -}-segmentPaths :: [FilePath] -> [FilePath] -> [[FilePath]]+segmentPaths :: [RawFilePath] -> [RawFilePath] -> [[RawFilePath]] segmentPaths [] new = [new] segmentPaths [_] new = [new] -- optimisation segmentPaths (l:ls) new = found : segmentPaths ls rest where (found, rest) = if length ls < 100- then partition (l `dirContains`) new- else break (\p -> not (l `dirContains` p)) new+ then partition inl new+ else break (not . inl) new+ inl f = fromRawFilePath l `dirContains` fromRawFilePath f {- This assumes that it's cheaper to call segmentPaths on the result, - than it would be to run the action separately with each path. In - the case of git file list commands, that assumption tends to hold. -}-runSegmentPaths :: ([FilePath] -> IO [FilePath]) -> [FilePath] -> IO [[FilePath]]+runSegmentPaths :: ([RawFilePath] -> IO [RawFilePath]) -> [RawFilePath] -> IO [[RawFilePath]] runSegmentPaths a paths = segmentPaths paths <$> a paths {- Converts paths in the home directory to use ~/ -}@@ -241,50 +266,6 @@ | otherwise = "." `isPrefixOf` f || dotfile (takeDirectory file) where f = takeFileName file--{- Converts a DOS style path to a msys2 style path. Only on Windows.- - Any trailing '\' is preserved as a trailing '/' - - - - Taken from: http://sourceforge.net/p/msys2/wiki/MSYS2%20introduction/i- -- - The virtual filesystem contains:- - /c, /d, ... mount points for Windows drives- -}-toMSYS2Path :: FilePath -> FilePath-#ifndef mingw32_HOST_OS-toMSYS2Path = id-#else-toMSYS2Path p- | null drive = recombine parts- | otherwise = recombine $ "/" : driveletter drive : parts- where- (drive, p') = splitDrive p- parts = splitDirectories p'- driveletter = map toLower . takeWhile (/= ':')- recombine = fixtrailing . Posix.joinPath- fixtrailing s- | hasTrailingPathSeparator p = Posix.addTrailingPathSeparator s- | otherwise = s-#endif--{- Maximum size to use for a file in a specified directory.- -- - Many systems have a 255 byte limit to the name of a file, - - so that's taken as the max if the system has a larger limit, or has no- - limit.- -}-fileNameLengthLimit :: FilePath -> IO Int-#ifdef mingw32_HOST_OS-fileNameLengthLimit _ = return 255-#else-fileNameLengthLimit dir = do- -- getPathVar can fail due to statfs(2) overflow- l <- catchDefaultIO 0 $- fromIntegral <$> getPathVar dir FileNameLimit- if l <= 0- then return 255- else return $ minimum [l, 255]-#endif {- Given a string that we'd like to use as the basis for FilePath, but that - was provided by a third party and is not to be trusted, returns the closest
− Utility/PosixFiles.hs
@@ -1,42 +0,0 @@-{- POSIX files (and compatablity wrappers).- -- - This is like System.PosixCompat.Files, but with a few fixes.- -- - Copyright 2014 Joey Hess <id@joeyh.name>- -- - License: BSD-2-clause- -}--{-# LANGUAGE CPP #-}-{-# OPTIONS_GHC -fno-warn-tabs #-}--module Utility.PosixFiles (- module X,- rename-) where--import System.PosixCompat.Files as X hiding (rename)--#ifndef mingw32_HOST_OS-import System.Posix.Files (rename)-#else-import qualified System.Win32.File as Win32-import qualified System.Win32.HardLink as Win32-#endif--{- System.PosixCompat.Files.rename on Windows calls renameFile,- - so cannot rename directories. - -- - Instead, use Win32 moveFile, which can. It needs to be told to overwrite- - any existing file. -}-#ifdef mingw32_HOST_OS-rename :: FilePath -> FilePath -> IO ()-rename src dest = Win32.moveFileEx src dest Win32.mOVEFILE_REPLACE_EXISTING-#endif--{- System.PosixCompat.Files.createLink throws an error, but windows- - does support hard links. -}-#ifdef mingw32_HOST_OS-createLink :: FilePath -> FilePath -> IO ()-createLink = Win32.createHardLink-#endif
Utility/Process.hs view
@@ -24,11 +24,10 @@ createProcessSuccess, createProcessChecked, createBackgroundProcess,- processTranscript,- processTranscript', withHandle, withIOHandles, withOEHandles,+ withNullHandle, withQuietOutput, feedWithQuietOutput, createProcess,@@ -54,13 +53,6 @@ import Control.Concurrent import qualified Control.Exception as E import Control.Monad-#ifndef mingw32_HOST_OS-import qualified System.Posix.IO-#else-import Control.Applicative-#endif-import Data.Maybe-import Prelude type CreateProcessRunner = forall a. CreateProcess -> ((Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) -> IO a) -> IO a @@ -170,68 +162,6 @@ createBackgroundProcess :: CreateProcessRunner createBackgroundProcess p a = a =<< createProcess p --- | Runs a process, optionally feeding it some input, and--- returns a transcript combining its stdout and stderr, and--- whether it succeeded or failed.-processTranscript :: String -> [String] -> (Maybe String) -> IO (String, Bool)-processTranscript cmd opts = processTranscript' (proc cmd opts)--processTranscript' :: CreateProcess -> Maybe String -> IO (String, Bool)-processTranscript' cp input = do-#ifndef mingw32_HOST_OS-{- This implementation interleves stdout and stderr in exactly the order- - the process writes them. -}- (readf, writef) <- System.Posix.IO.createPipe- readh <- System.Posix.IO.fdToHandle readf- writeh <- System.Posix.IO.fdToHandle writef- p@(_, _, _, pid) <- createProcess $ cp- { std_in = if isJust input then CreatePipe else Inherit- , std_out = UseHandle writeh- , std_err = UseHandle writeh- }- hClose writeh-- get <- mkreader readh- writeinput input p- transcript <- get-- ok <- checkSuccessProcess pid- return (transcript, ok)-#else-{- This implementation for Windows puts stderr after stdout. -}- p@(_, _, _, pid) <- createProcess $ cp- { std_in = if isJust input then CreatePipe else Inherit- , std_out = CreatePipe- , std_err = CreatePipe- }-- getout <- mkreader (stdoutHandle p)- geterr <- mkreader (stderrHandle p)- writeinput input p- transcript <- (++) <$> getout <*> geterr-- ok <- checkSuccessProcess pid- return (transcript, ok)-#endif- where- mkreader h = do- s <- hGetContents h- v <- newEmptyMVar- void $ forkIO $ do- void $ E.evaluate (length s)- putMVar v ()- return $ do- takeMVar v- return s-- writeinput (Just s) p = do- let inh = stdinHandle p- unless (null s) $ do- hPutStr inh s- hFlush inh- hClose inh- writeinput Nothing _ = return ()- -- | Runs a CreateProcessRunner, on a CreateProcess structure, that -- is adjusted to pipe only from/to a single StdHandle, and passes -- the resulting Handle to an action.@@ -248,13 +178,10 @@ , std_out = Inherit , std_err = Inherit }- (select, p')- | h == StdinHandle =- (stdinHandle, base { std_in = CreatePipe })- | h == StdoutHandle =- (stdoutHandle, base { std_out = CreatePipe })- | h == StderrHandle =- (stderrHandle, base { std_err = CreatePipe })+ (select, p') = case h of+ StdinHandle -> (stdinHandle, base { std_in = CreatePipe })+ StdoutHandle -> (stdoutHandle, base { std_out = CreatePipe })+ StderrHandle -> (stderrHandle, base { std_err = CreatePipe }) -- | Like withHandle, but passes (stdin, stdout) handles to the action. withIOHandles@@ -284,13 +211,16 @@ , std_err = CreatePipe } +withNullHandle :: (Handle -> IO a) -> IO a+withNullHandle = withFile devNull WriteMode+ -- | Forces the CreateProcessRunner to run quietly; -- both stdout and stderr are discarded. withQuietOutput :: CreateProcessRunner -> CreateProcess -> IO ()-withQuietOutput creator p = withFile devNull WriteMode $ \nullh -> do+withQuietOutput creator p = withNullHandle $ \nullh -> do let p' = p { std_out = UseHandle nullh , std_err = UseHandle nullh@@ -316,7 +246,8 @@ #ifndef mingw32_HOST_OS devNull = "/dev/null" #else-devNull = "NUL"+-- Use device namespace to prevent GHC from rewriting path+devNull = "\\\\.\\NUL" #endif -- | Extract a desired handle from createProcess's tuple.
Utility/QuickCheck.hs view
@@ -6,7 +6,7 @@ -} {-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE TypeSynonymInstances, CPP #-}+{-# LANGUAGE TypeSynonymInstances #-} module Utility.QuickCheck ( module X@@ -15,29 +15,24 @@ import Test.QuickCheck as X import Data.Time.Clock.POSIX+import Data.Ratio import System.Posix.Types-#if ! MIN_VERSION_QuickCheck(2,8,2)-import qualified Data.Map as M-import qualified Data.Set as S-#endif-import Control.Applicative+import Data.List.NonEmpty (NonEmpty(..)) import Prelude -#if ! MIN_VERSION_QuickCheck(2,8,2)-instance (Arbitrary k, Arbitrary v, Ord k) => Arbitrary (M.Map k v) where- arbitrary = M.fromList <$> arbitrary--instance (Arbitrary v, Ord v) => Arbitrary (S.Set v) where- arbitrary = S.fromList <$> arbitrary-#endif--{- Times before the epoch are excluded. -}+{- Times before the epoch are excluded. Half with decimal and half without. -} instance Arbitrary POSIXTime where- arbitrary = fromInteger <$> nonNegative arbitrarySizedIntegral+ arbitrary = do+ n <- nonNegative arbitrarySizedBoundedIntegral :: Gen Int+ d <- nonNegative arbitrarySizedIntegral+ withd <- arbitrary+ return $ if withd+ then fromIntegral n + fromRational (1 % max d 1)+ else fromIntegral n {- Pids are never negative, or 0. -} instance Arbitrary ProcessID where- arbitrary = arbitrarySizedBoundedIntegral `suchThat` (> 0)+ arbitrary = positive arbitrarySizedBoundedIntegral {- Inodes are never negative. -} instance Arbitrary FileID where@@ -46,6 +41,9 @@ {- File sizes are never negative. -} instance Arbitrary FileOffset where arbitrary = nonNegative arbitrarySizedIntegral++instance Arbitrary l => Arbitrary (NonEmpty l) where+ arbitrary = (:|) <$> arbitrary <*> arbitrary nonNegative :: (Num a, Ord a) => Gen a -> Gen a nonNegative g = g `suchThat` (>= 0)
Utility/Rsync.hs view
@@ -7,12 +7,26 @@ {-# LANGUAGE CPP #-} -module Utility.Rsync where+module Utility.Rsync (+ rsyncShell,+ rsyncServerSend,+ rsyncServerReceive,+ rsyncUseDestinationPermissions,+ rsync,+ rsyncUrlIsShell,+ rsyncUrlIsPath,+ rsyncProgress,+ filterRsyncSafeOptions,+) where import Common import Utility.Metered import Utility.Tuple +#ifdef mingw32_HOST_OS+import qualified System.FilePath.Posix as Posix+#endif+ import Data.Char import System.Console.GetOpt @@ -99,7 +113,16 @@ - The params must enable rsync's --progress mode for this to work. -} rsyncProgress :: OutputHandler -> MeterUpdate -> [CommandParam] -> IO Bool-rsyncProgress oh meter = commandMeter parseRsyncProgress oh meter "rsync" . rsyncParamsFixup+rsyncProgress oh meter ps =+ commandMeter' parseRsyncProgress oh meter "rsync" (rsyncParamsFixup ps) >>= \case+ Just ExitSuccess -> return True+ Just (ExitFailure exitcode) -> do+ when (exitcode /= 1) $+ hPutStrLn stderr $ "rsync exited " ++ show exitcode+ return False+ Nothing -> do+ hPutStrLn stderr $ "unable to run rsync"+ return False {- Strategy: Look for chunks prefixed with \r (rsync writes a \r before - the first progress output, and each thereafter). The first number@@ -139,3 +162,27 @@ [ Option [] ["bwlimit"] (reqArgLong "bwlimit") "" ] where reqArgLong x = ReqArg (\v -> "--" ++ x ++ "=" ++ v) ""++{- Converts a DOS style path to a msys2 style path. Only on Windows.+ - Any trailing '\' is preserved as a trailing '/' + - + - Taken from: http://sourceforge.net/p/msys2/wiki/MSYS2%20introduction/i+ -+ - The virtual filesystem contains:+ - /c, /d, ... mount points for Windows drives+ -}+#ifdef mingw32_HOST_OS+toMSYS2Path :: FilePath -> FilePath+toMSYS2Path p+ | null drive = recombine parts+ | otherwise = recombine $ "/" : driveletter drive : parts+ where+ (drive, p') = splitDrive p+ parts = splitDirectories p'+ driveletter = map toLower . takeWhile (/= ':')+ recombine = fixtrailing . Posix.joinPath+ fixtrailing s+ | hasTrailingPathSeparator p = Posix.addTrailingPathSeparator s+ | otherwise = s+#endif+
Utility/SafeCommand.hs view
@@ -7,7 +7,23 @@ {-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.SafeCommand where+module Utility.SafeCommand (+ CommandParam(..),+ toCommand,+ boolSystem,+ boolSystem',+ boolSystemEnv,+ safeSystem,+ safeSystem',+ safeSystemEnv,+ shellWrap,+ shellEscape,+ shellUnEscape,+ segmentXargsOrdered,+ segmentXargsUnordered,+ prop_isomorphic_shellEscape,+ prop_isomorphic_shellEscape_multiword,+) where import System.Exit import Utility.Process@@ -27,19 +43,21 @@ -- | Used to pass a list of CommandParams to a function that runs -- a command and expects Strings. -} toCommand :: [CommandParam] -> [String]-toCommand = map unwrap+toCommand = map toCommand'++toCommand' :: CommandParam -> String+toCommand' (Param s) = s+-- Files that start with a non-alphanumeric that is not a path+-- separator are modified to avoid the command interpreting them as+-- options or other special constructs.+toCommand' (File s@(h:_))+ | isAlphaNum h || h `elem` pathseps = s+ | otherwise = "./" ++ s where- unwrap (Param s) = s- -- Files that start with a non-alphanumeric that is not a path- -- separator are modified to avoid the command interpreting them as- -- options or other special constructs.- unwrap (File s@(h:_))- | isAlphaNum h || h `elem` pathseps = s- | otherwise = "./" ++ s- unwrap (File s) = s -- '/' is explicitly included because it's an alternative -- path separator on Windows. pathseps = pathSeparator:"./"+toCommand' (File s) = s -- | Run a system command, and returns True or False if it succeeded or failed. --
Utility/Split.hs view
@@ -7,7 +7,12 @@ {-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.Split where+module Utility.Split (+ split,+ splitc,+ replace,+ dropFromEnd,+) where import Data.List (intercalate) import Data.List.Split (splitOn)@@ -28,3 +33,7 @@ -- | same as Data.List.Utils.replace replace :: Eq a => [a] -> [a] -> [a] -> [a] replace old new = intercalate new . split old++-- | Only traverses the list once while dropping the last n items.+dropFromEnd :: Int -> [a] -> [a]+dropFromEnd n l = zipWith const l (drop n l)
Utility/ThreadScheduler.hs view
@@ -8,7 +8,14 @@ {-# LANGUAGE CPP #-} -module Utility.ThreadScheduler where+module Utility.ThreadScheduler (+ Seconds(..),+ Microseconds,+ runEvery,+ threadDelaySeconds,+ waitForTermination,+ oneSecond,+) where import Control.Monad import Control.Concurrent@@ -18,10 +25,8 @@ #endif #ifndef mingw32_HOST_OS import System.Posix.Signals-#ifndef __ANDROID__ import System.Posix.Terminal #endif-#endif newtype Seconds = Seconds { fromSeconds :: Int } deriving (Eq, Ord, Show)@@ -63,10 +68,8 @@ let check sig = void $ installHandler sig (CatchOnce $ putMVar lock ()) Nothing check softwareTermination-#ifndef __ANDROID__ whenM (queryTerminal stdInput) $ check keyboardSignal-#endif takeMVar lock #endif
Utility/Tmp.hs view
@@ -1,4 +1,4 @@-{- Temporary files and directories.+{- Temporary files. - - Copyright 2010-2013 Joey Hess <id@joeyh.name> -@@ -8,31 +8,33 @@ {-# LANGUAGE CPP #-} {-# OPTIONS_GHC -fno-warn-tabs #-} -module Utility.Tmp where+module Utility.Tmp (+ Template,+ viaTmp,+ withTmpFile,+ withTmpFileIn,+ relatedTemplate,+) where import System.IO-import Control.Monad.IfElse import System.FilePath import System.Directory import Control.Monad.IO.Class-#ifndef mingw32_HOST_OS-import System.Posix.Temp (mkdtemp)-#endif+import System.PosixCompat.Files import Utility.Exception import Utility.FileSystemEncoding-import Utility.PosixFiles type Template = String {- Runs an action like writeFile, writing to a temp file first and - then moving it into place. The temp file is stored in the same - directory as the final file to avoid cross-device renames. -}-viaTmp :: (MonadMask m, MonadIO m) => (FilePath -> String -> m ()) -> FilePath -> String -> m ()+viaTmp :: (MonadMask m, MonadIO m) => (FilePath -> v -> m ()) -> FilePath -> v -> m () viaTmp a file content = bracketIO setup cleanup use where (dir, base) = splitFileName file- template = base ++ ".tmp"+ template = relatedTemplate (base ++ ".tmp") setup = do createDirectoryIfMissing True dir openTempFile dir template@@ -61,51 +63,6 @@ hClose h catchBoolIO (removeFile name >> return True) use (name, h) = a name h--{- Runs an action with a tmp directory located within the system's tmp- - directory (or within "." if there is none), then removes the tmp- - directory and all its contents. -}-withTmpDir :: (MonadMask m, MonadIO m) => Template -> (FilePath -> m a) -> m a-withTmpDir template a = do- topleveltmpdir <- liftIO $ catchDefaultIO "." getTemporaryDirectory-#ifndef mingw32_HOST_OS- -- Use mkdtemp to create a temp directory securely in /tmp.- bracket- (liftIO $ mkdtemp $ topleveltmpdir </> template)- removeTmpDir- a-#else- withTmpDirIn topleveltmpdir template a-#endif--{- Runs an action with a tmp directory located within a specified directory,- - then removes the tmp directory and all its contents. -}-withTmpDirIn :: (MonadMask m, MonadIO m) => FilePath -> Template -> (FilePath -> m a) -> m a-withTmpDirIn tmpdir template = bracketIO create removeTmpDir- where- create = do- createDirectoryIfMissing True tmpdir- makenewdir (tmpdir </> template) (0 :: Int)- makenewdir t n = do- let dir = t ++ "." ++ show n- catchIOErrorType AlreadyExists (const $ makenewdir t $ n + 1) $ do- createDirectory dir- return dir--{- Deletes the entire contents of the the temporary directory, if it- - exists. -}-removeTmpDir :: MonadIO m => FilePath -> m ()-removeTmpDir tmpdir = liftIO $ whenM (doesDirectoryExist tmpdir) $ do-#if mingw32_HOST_OS- -- Windows will often refuse to delete a file- -- after a process has just written to it and exited.- -- Because it's crap, presumably. So, ignore failure- -- to delete the temp directory.- _ <- tryIO $ removeDirectoryRecursive tmpdir- return ()-#else- removeDirectoryRecursive tmpdir-#endif {- It's not safe to use a FilePath of an existing file as the template - for openTempFile, because if the FilePath is really long, the tmpfile
+ Utility/Tmp/Dir.hs view
@@ -0,0 +1,70 @@+{- Temporary directories+ -+ - Copyright 2010-2013 Joey Hess <id@joeyh.name>+ -+ - License: BSD-2-clause+ -}++{-# LANGUAGE CPP #-}+{-# OPTIONS_GHC -fno-warn-tabs #-}++module Utility.Tmp.Dir (+ withTmpDir,+ withTmpDirIn,+) where++import Control.Monad.IfElse+import System.FilePath+import System.Directory+import Control.Monad.IO.Class+#ifndef mingw32_HOST_OS+import System.Posix.Temp (mkdtemp)+#endif++import Utility.Exception+import Utility.Tmp (Template)++{- Runs an action with a tmp directory located within the system's tmp+ - directory (or within "." if there is none), then removes the tmp+ - directory and all its contents. -}+withTmpDir :: (MonadMask m, MonadIO m) => Template -> (FilePath -> m a) -> m a+withTmpDir template a = do+ topleveltmpdir <- liftIO $ catchDefaultIO "." getTemporaryDirectory+#ifndef mingw32_HOST_OS+ -- Use mkdtemp to create a temp directory securely in /tmp.+ bracket+ (liftIO $ mkdtemp $ topleveltmpdir </> template)+ removeTmpDir+ a+#else+ withTmpDirIn topleveltmpdir template a+#endif++{- Runs an action with a tmp directory located within a specified directory,+ - then removes the tmp directory and all its contents. -}+withTmpDirIn :: (MonadMask m, MonadIO m) => FilePath -> Template -> (FilePath -> m a) -> m a+withTmpDirIn tmpdir template = bracketIO create removeTmpDir+ where+ create = do+ createDirectoryIfMissing True tmpdir+ makenewdir (tmpdir </> template) (0 :: Int)+ makenewdir t n = do+ let dir = t ++ "." ++ show n+ catchIOErrorType AlreadyExists (const $ makenewdir t $ n + 1) $ do+ createDirectory dir+ return dir++{- Deletes the entire contents of the the temporary directory, if it+ - exists. -}+removeTmpDir :: MonadIO m => FilePath -> m ()+removeTmpDir tmpdir = liftIO $ whenM (doesDirectoryExist tmpdir) $ do+#if mingw32_HOST_OS+ -- Windows will often refuse to delete a file+ -- after a process has just written to it and exited.+ -- Because it's crap, presumably. So, ignore failure+ -- to delete the temp directory.+ _ <- tryIO $ removeDirectoryRecursive tmpdir+ return ()+#else+ removeDirectoryRecursive tmpdir+#endif
Utility/Tuple.hs view
@@ -5,7 +5,11 @@ - License: BSD-2-clause -} -module Utility.Tuple where+module Utility.Tuple (+ fst3,+ snd3,+ thd3,+) where fst3 :: (a,b,c) -> a fst3 (a,_,_) = a
Utility/UserInfo.hs view
@@ -14,12 +14,14 @@ myUserGecos, ) where -import Utility.Env-import Utility.Data+import Utility.Env.Basic import Utility.Exception+#ifndef mingw32_HOST_OS+import Utility.Data+import Control.Applicative+#endif import System.PosixCompat-import Control.Applicative import Prelude {- Current user's home directory.@@ -45,8 +47,8 @@ #endif myUserGecos :: IO (Maybe String)--- userGecos crashes on Android and is not available on Windows.-#if defined(__ANDROID__) || defined(mingw32_HOST_OS)+-- userGecos is not available on Windows.+#if defined(mingw32_HOST_OS) myUserGecos = return Nothing #else myUserGecos = eitherToMaybe <$> myVal [] userGecos@@ -55,9 +57,13 @@ myVal :: [String] -> (UserEntry -> String) -> IO (Either String String) myVal envvars extract = go envvars where+ go [] = either (const $ envnotset) (Right . extract) <$> get+ go (v:vs) = maybe (go vs) (return . Right) =<< getEnv v #ifndef mingw32_HOST_OS- go [] = Right . extract <$> (getUserEntryForID =<< getEffectiveUserID)+ -- This may throw an exception if the system doesn't have a+ -- passwd file etc; don't let it crash.+ get = tryNonAsync $ getUserEntryForID =<< getEffectiveUserID #else- go [] = return $ Left ("environment not set: " ++ show envvars)+ get = return envnotset #endif- go (v:vs) = maybe (go vs) (return . Right) =<< getEnv v+ envnotset = Left ("environment not set: " ++ show envvars)
git-repair.cabal view
@@ -1,12 +1,12 @@ Name: git-repair-Version: 1.20170626+Version: 1.20200102 Cabal-Version: >= 1.8-License: GPL+License: AGPL-3 Maintainer: Joey Hess <joey@kitenet.net> Author: Joey Hess Stability: Stable Copyright: 2013 Joey Hess-License-File: GPL+License-File: COPYRIGHT Build-Type: Custom Homepage: http://git-repair.branchable.com/ Category: Utility@@ -25,13 +25,10 @@ TODO git-repair.1 -Flag network-uri- Description: Get Network.URI from the network-uri package- Default: True- custom-setup- Setup-Depends: base (>= 4.5), hslogger, split, unix-compat, process,- unix, filepath, exceptions, bytestring, directory, IfElse, data-default,+ Setup-Depends: base (>= 4.11.1.0 && < 5.0), + hslogger, split, unix-compat, process, unix, filepath,+ exceptions, bytestring, directory, IfElse, data-default, mtl, Cabal source-repository head@@ -41,23 +38,22 @@ Executable git-repair Main-Is: git-repair.hs GHC-Options: -threaded -Wall -fno-warn-tabs+ Extensions: LambdaCase Build-Depends: split, hslogger, directory, filepath, containers, mtl,- unix-compat, bytestring, exceptions (>= 0.6), transformers,- base >= 4.5, base < 5, IfElse, text, process, time, QuickCheck,- utf8-string, async, optparse-applicative (>= 0.10.0),- data-default-- if flag(network-uri)- Build-Depends: network-uri (>= 2.6), network (>= 2.6)- else- Build-Depends: network (< 2.6), network (>= 2.0)+ unix-compat (>= 0.5), bytestring, exceptions (>= 0.6), transformers,+ base (>= 4.11.1.0 && < 5.0), IfElse, text, process, time, QuickCheck,+ utf8-string, async, optparse-applicative (>= 0.14.1),+ data-default, deepseq, attoparsec,+ network-uri (>= 2.6), network (>= 2.6),+ filepath-bytestring (>= 1.4.2.1.0) if (os(windows)) Build-Depends: setenv else- Build-Depends: unix+ Build-Depends: unix (>= 2.7.2) Other-Modules:+ BuildInfo Build.Configure Build.TestConfig Build.Version@@ -75,6 +71,7 @@ Git.FilePath Git.Filename Git.Fsck+ Git.HashObject Git.Index Git.LsFiles Git.LsTree@@ -88,6 +85,7 @@ Git.UpdateIndex Git.Url Git.Version+ Utility.Attoparsec Utility.Applicative Utility.Batch Utility.CoProcess@@ -96,6 +94,8 @@ Utility.Directory Utility.DottedVersion Utility.Env+ Utility.Env.Basic+ Utility.Env.Set Utility.Exception Utility.FileMode Utility.FileSize@@ -109,7 +109,6 @@ Utility.PartialPrelude Utility.Path Utility.Percentage- Utility.PosixFiles Utility.Process Utility.Process.Shim Utility.QuickCheck@@ -119,5 +118,6 @@ Utility.SystemDirectory Utility.ThreadScheduler Utility.Tmp+ Utility.Tmp.Dir Utility.Tuple Utility.UserInfo
git-repair.hs view
@@ -2,7 +2,7 @@ - - Copyright 2013 Joey Hess <joey@kitenet.net> -- - Licensed under the GNU GPL version 3 or higher.+ - Licensed under the GNU AGPL version 3 or higher. -} import Options.Applicative@@ -14,8 +14,7 @@ import qualified Git.Construct import qualified Git.Destroyer import qualified Git.Fsck-import Utility.Tmp-import Utility.FileSystemEncoding+import Utility.Tmp.Dir data Settings = Settings { forced :: Bool