By | fr33domlover |
At | 2016-04-28 |
Title | Misc files and initial code copied from hit-graph and hit-network |
Description |
Add file AUTHORS 0
Edit file AUTHORS 0 → 0
+ 1 fr33domlover <fr33domlover@riseup.net>
… … … … Add file COPYING 0
Edit file COPYING 0 → 0
+ 1 Creative Commons Legal Code
+ 2 + 3 CC0 1.0 Universal
+ 4 + 5 CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ 6 LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ 7 ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ 8 INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ 9 REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ 10 PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ 11 THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ 12 HEREUNDER.
+ 13 + 14 Statement of Purpose
+ 15 + 16 The laws of most jurisdictions throughout the world automatically confer
+ 17 exclusive Copyright and Related Rights (defined below) upon the creator
+ 18 and subsequent owner(s) (each and all, an "owner") of an original work of
+ 19 authorship and/or a database (each, a "Work").
+ 20 + 21 Certain owners wish to permanently relinquish those rights to a Work for
+ 22 the purpose of contributing to a commons of creative, cultural and
+ 23 scientific works ("Commons") that the public can reliably and without fear
+ 24 of later claims of infringement build upon, modify, incorporate in other
+ 25 works, reuse and redistribute as freely as possible in any form whatsoever
+ 26 and for any purposes, including without limitation commercial purposes.
+ 27 These owners may contribute to the Commons to promote the ideal of a free
+ 28 culture and the further production of creative, cultural and scientific
+ 29 works, or to gain reputation or greater distribution for their Work in
+ 30 part through the use and efforts of others.
+ 31 + 32 For these and/or other purposes and motivations, and without any
+ 33 expectation of additional consideration or compensation, the person
+ 34 associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+ 35 is an owner of Copyright and Related Rights in the Work, voluntarily
+ 36 elects to apply CC0 to the Work and publicly distribute the Work under its
+ 37 terms, with knowledge of his or her Copyright and Related Rights in the
+ 38 Work and the meaning and intended legal effect of CC0 on those rights.
+ 39 + 40 1. Copyright and Related Rights. A Work made available under CC0 may be
+ 41 protected by copyright and related or neighboring rights ("Copyright and
+ 42 Related Rights"). Copyright and Related Rights include, but are not
+ 43 limited to, the following:
+ 44 + 45 i. the right to reproduce, adapt, distribute, perform, display,
+ 46 communicate, and translate a Work;
+ 47 ii. moral rights retained by the original author(s) and/or performer(s);
+ 48 iii. publicity and privacy rights pertaining to a person's image or
+ 49 likeness depicted in a Work;
+ 50 iv. rights protecting against unfair competition in regards to a Work,
+ 51 subject to the limitations in paragraph 4(a), below;
+ 52 v. rights protecting the extraction, dissemination, use and reuse of data
+ 53 in a Work;
+ 54 vi. database rights (such as those arising under Directive 96/9/EC of the
+ 55 European Parliament and of the Council of 11 March 1996 on the legal
+ 56 protection of databases, and under any national implementation
+ 57 thereof, including any amended or successor version of such
+ 58 directive); and
+ 59 vii. other similar, equivalent or corresponding rights throughout the
+ 60 world based on applicable law or treaty, and any national
+ 61 implementations thereof.
+ 62 + 63 2. Waiver. To the greatest extent permitted by, but not in contravention
+ 64 of, applicable law, Affirmer hereby overtly, fully, permanently,
+ 65 irrevocably and unconditionally waives, abandons, and surrenders all of
+ 66 Affirmer's Copyright and Related Rights and associated claims and causes
+ 67 of action, whether now known or unknown (including existing as well as
+ 68 future claims and causes of action), in the Work (i) in all territories
+ 69 worldwide, (ii) for the maximum duration provided by applicable law or
+ 70 treaty (including future time extensions), (iii) in any current or future
+ 71 medium and for any number of copies, and (iv) for any purpose whatsoever,
+ 72 including without limitation commercial, advertising or promotional
+ 73 purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+ 74 member of the public at large and to the detriment of Affirmer's heirs and
+ 75 successors, fully intending that such Waiver shall not be subject to
+ 76 revocation, rescission, cancellation, termination, or any other legal or
+ 77 equitable action to disrupt the quiet enjoyment of the Work by the public
+ 78 as contemplated by Affirmer's express Statement of Purpose.
+ 79 + 80 3. Public License Fallback. Should any part of the Waiver for any reason
+ 81 be judged legally invalid or ineffective under applicable law, then the
+ 82 Waiver shall be preserved to the maximum extent permitted taking into
+ 83 account Affirmer's express Statement of Purpose. In addition, to the
+ 84 extent the Waiver is so judged Affirmer hereby grants to each affected
+ 85 person a royalty-free, non transferable, non sublicensable, non exclusive,
+ 86 irrevocable and unconditional license to exercise Affirmer's Copyright and
+ 87 Related Rights in the Work (i) in all territories worldwide, (ii) for the
+ 88 maximum duration provided by applicable law or treaty (including future
+ 89 time extensions), (iii) in any current or future medium and for any number
+ 90 of copies, and (iv) for any purpose whatsoever, including without
+ 91 limitation commercial, advertising or promotional purposes (the
+ 92 "License"). The License shall be deemed effective as of the date CC0 was
+ 93 applied by Affirmer to the Work. Should any part of the License for any
+ 94 reason be judged legally invalid or ineffective under applicable law, such
+ 95 partial invalidity or ineffectiveness shall not invalidate the remainder
+ 96 of the License, and in such case Affirmer hereby affirms that he or she
+ 97 will not (i) exercise any of his or her remaining Copyright and Related
+ 98 Rights in the Work or (ii) assert any associated claims and causes of
+ 99 action with respect to the Work, in either case contrary to Affirmer's
+ 100 express Statement of Purpose.
+ 101 + 102 4. Limitations and Disclaimers.
+ 103 + 104 a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ 105 surrendered, licensed or otherwise affected by this document.
+ 106 b. Affirmer offers the Work as-is and makes no representations or
+ 107 warranties of any kind concerning the Work, express, implied,
+ 108 statutory or otherwise, including without limitation warranties of
+ 109 title, merchantability, fitness for a particular purpose, non
+ 110 infringement, or the absence of latent or other defects, accuracy, or
+ 111 the present or absence of errors, whether or not discoverable, all to
+ 112 the greatest extent permissible under applicable law.
+ 113 c. Affirmer disclaims responsibility for clearing rights of other persons
+ 114 that may apply to the Work or any use thereof, including without
+ 115 limitation any person's Copyright and Related Rights in the Work.
+ 116 Further, Affirmer disclaims responsibility for obtaining any necessary
+ 117 consents, permissions or other rights required for any use of the
+ 118 Work.
+ 119 d. Affirmer understands and acknowledges that Creative Commons is not a
+ 120 party to this document and has no duty or obligation with respect to
+ 121 this CC0 or use of the Work.
… … … … Add file ChangeLog 0
Edit file ChangeLog 0 → 0
+ 1 The changes are recorded by the version control system, Darcs. To see a log
+ 2 quickly from the terminal, run:
+ 3 + 4 $ darcs changes --repo http://hub.darcs.net/fr33domlover/hit-harder
+ 5 + 6 There is also a web interface at <http://hub.darcs.net> which, among other
+ 7 things, can display the history log.
+ 8 + 9 To see the log in a local clone, first get a copy of the repository if you
+ 10 haven't yet:
+ 11 + 12 $ darcs get http://hub.darcs.net/fr33domlover/hit-harder
+ 13 + 14 Then move into the newly created directory and run darcs:
+ 15 + 16 $ cd hit-harder
+ 17 $ darcs changes
… … … … Add file INSTALL.md 0
Edit file INSTALL.md 0 → 0
+ 1 Install from Hackage/Stackage:
+ 2 + 3 $ stack install hit-harder
+ 4 + 5 Install from unpacked release tarball or source repo:
+ 6 + 7 $ cd hit-harder
+ 8 $ stack install
+ 9 + 10 Just play with it without installing:
+ 11 + 12 $ stack repl
… … … … Add file NEWS.md 0
Edit file NEWS.md 0 → 0
+ 1 This file lists the user-visible interesting changes between releases. For a
+ 2 full list of changes to the source, see the ChangeLog.
+ 3 + 4 + 5 + 6 hit-harder 0.1 unreleased
+ 7 ================================
+ 8 + 9 General, build and documentation changes:
+ 10 + 11 * (This is the first release, so everything is new)
+ 12 + 13 New APIs, features and enhancements:
+ 14 + 15 * (This is the first release, so everything is a new feature)
+ 16 + 17 Bug fixes:
+ 18 + 19 * (This is just the first release, many bugs haven't been discovered yet)
+ 20 + 21 Dependency changes:
+ 22 + 23 * (This is the first release)
… … … … Add file README.md 0
Edit file README.md 0 → 0
+ 1 See the .cabal file for more info and link to project website the version
+ 2 control.
+ 3 + 4 The official download location is Hackage:
+ 5 + 6 <http://hackage.haskell.org/package/hit-harder>
+ 7 + 8 This library is free software, and is committed to software freedom. It is
+ 9 released to the public domain using the CC0 Public Domain Dedication. For the
+ 10 boring "legal" details see the file 'COPYING'.
+ 11 + 12 See the file 'INSTALL' for hints on installation. The file 'ChangeLog' explains
+ 13 how to see the history log of the changes done in the code. 'NEWS' provides a
+ 14 friendly overview of the changes for each release.
… … … … Add file Setup.hs 0
Edit file Setup.hs 0 → 0
+ 1 import Distribution.Simple
+ 2 main = defaultMain
… … … … Add file _boring 0
Edit file _boring 0 → 0
+ 1 # This file contains a list of extended regular expressions, one per
+ 2 # line. A file path matching any of these expressions will be filtered
+ 3 # out during `darcs add', or when the `--look-for-adds' flag is passed
+ 4 # to `darcs whatsnew' and `record'. The entries in ~/.darcs/boring (if
+ 5 # it exists) supplement those in this file.
+ 6 #
+ 7 # Blank lines, and lines beginning with an octothorpe (#) are ignored.
+ 8 # See regex(7) for a description of extended regular expressions.
+ 9 + 10 ### compiler and interpreter intermediate files
+ 11 # haskell (ghc) interfaces
+ 12 \.hi$
+ 13 \.hi-boot$
+ 14 \.o-boot$
+ 15 # object files
+ 16 \.o$
+ 17 \.o\.cmd$
+ 18 # profiling haskell
+ 19 \.p_hi$
+ 20 \.p_o$
+ 21 # haskell program coverage resp. profiling info
+ 22 \.tix$
+ 23 \.prof$
+ 24 # fortran module files
+ 25 \.mod$
+ 26 # linux kernel
+ 27 \.ko\.cmd$
+ 28 \.mod\.c$
+ 29 (^|/)\.tmp_versions($|/)
+ 30 # *.ko files aren't boring by default because they might
+ 31 # be Korean translations rather than kernel modules
+ 32 # \.ko$
+ 33 # python, emacs, java byte code
+ 34 \.py[co]$
+ 35 \.elc$
+ 36 \.class$
+ 37 # objects and libraries; lo and la are libtool things
+ 38 \.(obj|a|exe|so|lo|la)$
+ 39 # compiled zsh configuration files
+ 40 \.zwc$
+ 41 # Common LISP output files for CLISP and CMUCL
+ 42 \.(fas|fasl|sparcf|x86f)$
+ 43 + 44 ### build and packaging systems
+ 45 # cabal intermediates
+ 46 \.installed-pkg-config
+ 47 \.setup-config
+ 48 # standard cabal and stack build dirs
+ 49 ^dist$
+ 50 ^dist/build(/|$)
+ 51 ^dist/doc(/|$)
+ 52 ^dist/dist-sandbox
+ 53 ^dist/package\.conf\.inplace$
+ 54 ^dist/setup-config$
+ 55 ^\.cabal-sandbox(/|$)
+ 56 ^cabal\.sandbox\.config$
+ 57 ^.stack-work(/|$)
+ 58 # autotools
+ 59 (^|/)autom4te\.cache($|/)
+ 60 (^|/)config\.(log|status)$
+ 61 # gentoo tools
+ 62 \.revdep-rebuild.*
+ 63 # generated dependencies
+ 64 ^\.depend$
+ 65 + 66 ### version control systems
+ 67 # cvs
+ 68 (^|/)CVS($|/)
+ 69 \.cvsignore$
+ 70 # cvs, emacs locks
+ 71 ^\.#
+ 72 # rcs
+ 73 (^|/)RCS($|/)
+ 74 ,v$
+ 75 # subversion
+ 76 (^|/)\.svn($|/)
+ 77 # mercurial
+ 78 (^|/)\.hg($|/)
+ 79 # git
+ 80 (^|/)\.git($|/)
+ 81 # bzr
+ 82 \.bzr$
+ 83 # sccs
+ 84 (^|/)SCCS($|/)
+ 85 # darcs
+ 86 (^|/)_darcs($|/)
+ 87 (^|/)\.darcsrepo($|/)
+ 88 ^\.darcs-temp-mail$
+ 89 -darcs-backup[[:digit:]]+$
+ 90 # gnu arch
+ 91 (^|/)(\+|,)
+ 92 (^|/)vssver\.scc$
+ 93 \.swp$
+ 94 (^|/)MT($|/)
+ 95 (^|/)\{arch\}($|/)
+ 96 (^|/).arch-ids($|/)
+ 97 + 98 ### miscellaneous
+ 99 # backup files
+ 100 ~$
+ 101 \.bak$
+ 102 \.BAK$
+ 103 # patch originals and rejects
+ 104 \.orig$
+ 105 \.rej$
+ 106 # X server
+ 107 \..serverauth.*
+ 108 # vi, emacs tags
+ 109 (^|/)(tags|TAGS)$
+ 110 #(^|/)\.[^/]
+ 111 # core dumps
+ 112 (^|/|\.)core$
+ 113 # partial broken files (KIO copy operations)
+ 114 \.part$
+ 115 # waf files, see http://code.google.com/p/waf/
+ 116 (^|/)\.waf-[[:digit:].]+-[[:digit:]]+($|/)
+ 117 (^|/)\.lock-wscript$
… … … … Add file hit-harder.cabal 0
Edit file hit-harder.cabal 0 → 0
+ 1 name: hit-harder
+ 2 version: 0.1
+ 3 synopsis: More Git tools in pure Haskell on top of 'hit'
+ 4 description:
+ 5 During my work on a Haskell implementation of the Git pack protocol, which is
+ 6 still in progress at the time of writing, I've been writing various general
+ 7 purpose utilities. At the beginning I just dumped them into Util modules, but
+ 8 at some point the collection of utils felt too useful to be kept like that,
+ 9 and I decided to create a separate package for them.
+ 10 homepage: http://hub.darcs.net/fr33domlover/hit-harder
+ 11 bug-reports: mailto:fr33domlover@riseup.net
+ 12 license: PublicDomain
+ 13 license-file: COPYING
+ 14 author: fr33domlover
+ 15 maintainer: fr33domlover@riseup.net
+ 16 copyright: ♡ Copying is an act of love. Please copy, reuse and share.
+ 17 category: Git, Development
+ 18 build-type: Simple
+ 19 extra-source-files: AUTHORS ChangeLog COPYING INSTALL.md NEWS.md README.md
+ 20 cabal-version: >=1.10
+ 21 + 22 source-repository head
+ 23 type: darcs
+ 24 location: http://hub.darcs.net/fr33domlover/hit-harder
+ 25 + 26 library
+ 27 exposed-modules: Data.Git.Harder
+ 28 -- other-modules:
+ 29 -- other-extensions:
+ 30 build-depends: base >= 4.8 && < 5
+ 31 --, containers
+ 32 --, fgl
+ 33 --, hashable
+ 34 , hit
+ 35 --, transformers
+ 36 --, unordered-containers
+ 37 hs-source-dirs: src
+ 38 default-language: Haskell2010
+ 39 ghc-options: -Wall
… … … … Add file src 0
Add file src/Data 0
Add file src/Data/Git 0
Add file src/Data/Git/Harder 0
Add file src/Data/Git/Harder.hs 0
Edit file src/Data/Git/Harder.hs 0 → 0
+ 1 {- This file is part of hit-harder.
+ 2 -
+ 3 - Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
+ 4 -
+ 5 - ♡ Copying is an act of love. Please copy, reuse and share.
+ 6 -
+ 7 - The author(s) have dedicated all copyright and related and neighboring
+ 8 - rights to this software to the public domain worldwide. This software is
+ 9 - distributed without any warranty.
+ 10 -
+ 11 - You should have received a copy of the CC0 Public Domain Dedication along
+ 12 - with this software. If not, see
+ 13 - <http://creativecommons.org/publicdomain/zero/1.0/>.
+ 14 -}
+ 15 + 16 module Data.Git.Harder
+ 17 ( -- * Refs
+ 18 ObjId (..)
+ 19 , resolveNameMaybe
+ 20 , resolveName
+ 21 , listReferences
+ 22 -- * Commits
+ 23 , loadCommits
+ 24 , loadCommitsMulti
+ 25 -- * Trees
+ 26 , getEntryObject
+ 27 , getEntryObject_
+ 28 , TraversalAction (..)
+ 29 , traverseTree
+ 30 , viewTree
+ 31 , resolveTreePath
+ 32 )
+ 33 where
+ 34 + 35 --import Control.Monad.IO.Class
+ 36 import Data.Foldable (find, foldl', foldlM)
+ 37 import Data.Git.Named (RefName (..))
+ 38 import Data.Git.Ref (Ref, toBinary)
+ 39 import Data.Git.Repository
+ 40 import Data.Git.Revision (Revision (..))
+ 41 import Data.Git.Storage (Git, getObject, getObject_, getObjectType)
+ 42 import Data.Git.Storage.Object (Object (..))
+ 43 import Data.Git.Types
+ 44 --import Data.Hashable (Hashable (..))
+ 45 import Data.Maybe (catMaybes, fromMaybe)
+ 46 --import Data.Ord (Down (..))
+ 47 import Data.Traversable (for)
+ 48 + 49 --import qualified Data.DList as D
+ 50 --import qualified Data.HashMap.Strict as M
+ 51 --import qualified Data.Set as S
+ 52 + 53 --import Data.Graph.Inductive.Query.Topsort
+ 54 + 55 -- | A git object identifier. This is a SHA-1 hash. Its common textual
+ 56 -- representation is a 40-byte ASCII hexadecimal string.
+ 57 newtype ObjId = ObjId { unObjId :: Ref } deriving Eq
+ 58 + 59 instance Hashable ObjId where
+ 60 hashWithSalt salt = hashWithSalt salt . toBinary . unObjId
+ 61 hash = hash . toBinary . unObjId
+ 62 + 63 -- | For a given ref name - HEAD or branch or tag - determine its ref hash.
+ 64 resolveNameMaybe :: Git -> String -> IO (Maybe ObjId)
+ 65 resolveNameMaybe git name =
+ 66 fmap ObjId <$> resolveRevision git (Revision name [])
+ 67 + 68 -- | For a given ref name - HEAD or branch or tag - determine its ref hash.
+ 69 resolveName :: Git -> String -> IO ObjId
+ 70 resolveName git name = do
+ 71 moid <- resolveNameMaybe git name
+ 72 return $ fromMaybe (error "No such ref name in the repo") moid
+ 73 + 74 -- | List the available references in a git repo, sorted by ref name. The list
+ 75 -- includes HEAD, branches and tags.
+ 76 listReferences :: Git -> IO [(ObjId, String)]
+ 77 listReferences git = do
+ 78 branches <- S.mapMonotonic refNameRaw <$> branchList git
+ 79 tags <- S.mapMonotonic refNameRaw <$> tagList git
+ 80 let names = S.toAscList $ S.insert "HEAD" $ S.union branches tags
+ 81 mentries <-
+ 82 traverse
+ 83 (\ name -> fmap (flip (,) name) <$> resolveNameMaybe git name)
+ 84 names
+ 85 return $ catMaybes mentries
+ 86 + 87 -- | Load the entire graph of commits which are ancestors of the given ref
+ 88 -- (and that ref itself). Fold the commit structure into a value of type @a@
+ 89 -- inside monad @m@.
+ 90 --
+ 91 -- This is a low-level function which operates on a commit tree, i.e. the same
+ 92 -- ref may be visited more than once (if it has more than one child commit).
+ 93 -- You can use the provided flexibility to implement graph algorithms over the
+ 94 -- commits, or build a graph using some graph library and use that library's
+ 95 -- tools for further processing.
+ 96 loadCommits
+ 97 :: MonadIO m
+ 98 => Git
+ 99 -- ^ Open git repository context
+ 100 -> ((ObjId, Commit) -> ObjId -> a -> m (a, Maybe Commit))
+ 101 -- ^ Given a child commit, one of its parent commits and an @a@ value,
+ 102 -- generate an updated @a@ value. The second returned value determines
+ 103 -- whether traversal should proceed to the parent of the parent commit. If
+ 104 -- you return 'Nothing', it won't. If you load the parent commit (e.g. with
+ 105 -- 'getCommit') and return 'Just' it, traversal will proceed to its
+ 106 -- parents.
+ 107 -> a
+ 108 -- ^ Initial value
+ 109 -> ObjId
+ 110 -- ^ Hash of the commit whose ancestor graph should be loaded
+ 111 -> Maybe Commit
+ 112 -- ^ If you already read the commit for the ref passed as the previous
+ 113 -- parameter, pass the commit here to avoid repeated loading of it.
+ 114 -- Otherwise, pass 'Nothing' and it will be read from the repo.
+ 115 -> m a
+ 116 loadCommits git func val oid mcmt = readCommitMaybe oid mcmt >>= go val oid
+ 117 where
+ 118 readCommit = liftIO . getCommit git . unObjId
+ 119 readCommitMaybe r = maybe (readCommit r) return
+ 120 step p v r = do
+ 121 (v', mc) <- func p r v
+ 122 case mc of
+ 123 Nothing -> return v'
+ 124 Just c -> go v' r c
+ 125 go v r c = foldlM (step (r, c)) v $ map ObjId $ commitParents c
+ 126 + 127 -- | Like 'loadCommits', but takes a list of refs and goes over all their
+ 128 -- ancestors. This is just a convenience shortcut which folds a list with
+ 129 -- 'loadCommits'. Passing a list with a single element is the same as running
+ 130 -- 'loadCommits'.
+ 131 loadCommitsMulti
+ 132 :: MonadIO m
+ 133 => Git
+ 134 -- ^ Open git repository context
+ 135 -> ((ObjId, Commit) -> ObjId -> a -> m (a, Maybe Commit))
+ 136 -- ^ Given a child commit, one of its parent commits and an @a@ value,
+ 137 -- generate an updated @a@ value. The second returned value determines
+ 138 -- whether traversal should proceed to the parent of the parent commit. If
+ 139 -- you return 'Nothing', it won't. If you load the parent commit (e.g. with
+ 140 -- 'getCommit') and return 'Just' it, traversal will proceed to its
+ 141 -- parents.
+ 142 -> a
+ 143 -- ^ Initial value
+ 144 -> [(ObjId, Maybe Commit)]
+ 145 -- ^ Commits whose ancestors to scan. For each commit, pass:
+ 146 --
+ 147 -- (1) Hash of the commit
+ 148 -- (2) If you already loaded the commit from the ref, pass the commit here
+ 149 -- to avoid repeated loading of it. Otherwise, pass 'Nothing' and it
+ 150 -- will be read from the repo.
+ 151 -> m a
+ 152 loadCommitsMulti git func val pairs =
+ 153 foldlM (\ v (r, mc) -> loadCommits git func v r mc) val pairs
+ 154 + 155 -- | TODO
+ 156 getEntryObject :: Git -> ObjId -> IO (Maybe (Either Blob Tree))
+ 157 getEntryObject git oid = do
+ 158 mobj <- getObject git (unObjId oid) True
+ 159 case mobj of
+ 160 Nothing -> return Nothing
+ 161 Just obj ->
+ 162 case obj of
+ 163 ObjBlob b -> return $ Just $ Left b
+ 164 ObjTree t -> return $ Just $ Right t
+ 165 _ -> error "expected blob or tree"
+ 166 + 167 -- | TODO
+ 168 getEntryObject_ :: Git -> ObjId -> IO (Either Blob Tree)
+ 169 getEntryObject_ git oid = do
+ 170 obj <- getObject_ git (unObjId oid) True
+ 171 case obj of
+ 172 ObjBlob b -> return $ Left b
+ 173 ObjTree t -> return $ Right t
+ 174 _ -> error "expected blob or tree"
+ 175 + 176 -- | TODO
+ 177 data TraversalAction = TAStop | TAContinue | TAContinueWith Tree
+ 178 + 179 -- | Aside of dependency on previous commits (i.e. parents), each commit object
+ 180 -- refers to a tree object, and the tree refers to more trees and to blobs. For
+ 181 -- a given tree root, this function goes over these trees and blobs, and runs
+ 182 -- the given action on each.
+ 183 --
+ 184 -- TODOOOO update the doc comments of this function and its args
+ 185 traverseTree
+ 186 :: MonadIO m
+ 187 => Git
+ 188 -- ^ Open git repository
+ 189 -> (a -> ObjId -> ModePerm -> EntName -> m (a, TraversalAction))
+ 190 -- ^ This action will be executed for each tree entry found. The first
+ 191 -- parameter is the value being accumulated. The next three parameters are
+ 192 -- the tree entry details (object ID, permissions and filename). The last
+ 193 -- parameter is 'True' if that entry is a tree, and 'False' if it's a blob.
+ 194 -- What's returned is an updated value and whether to descend to the tree's
+ 195 -- entries (for a blob, this value is ignored).
+ 196 -> Tree
+ 197 -- ^ Tree root whose entries will be traversed recursively.
+ 198 -> a
+ 199 -- ^ Initial value
+ 200 -> m a
+ 201 traverseTree git action root initial = foldlM go' initial $ treeGetEnts root
+ 202 where
+ 203 go' v (p, n, r) = go v (ObjId r) p n
+ 204 go value oid perm name = do
+ 205 (value', next) <- action value oid perm name
+ 206 let descend = foldlM go' value' . treeGetEnts
+ 207 case next of
+ 208 TAStop -> return value'
+ 209 TAContinue -> do
+ 210 object <- liftIO $ getEntryObject_ git oid
+ 211 case object of
+ 212 Left _ -> return value'
+ 213 Right tree -> descend tree
+ 214 TAContinueWith tree -> descend tree
+ 215 + 216 -- | A simple utility for listing info of a tree's content. The last tuple
+ 217 -- element is 'True' for a tree and 'False' for a blob.
+ 218 viewTree :: Git -> Tree -> IO [(ModePerm, EntName, Bool)]
+ 219 viewTree git tree = for (treeGetEnts tree) $ \ (perm, name, ref) -> do
+ 220 mtype <- getObjectType git ref
+ 221 case mtype of
+ 222 Nothing -> error "tree contains an invalid entry ref"
+ 223 Just TypeTree -> return (perm, name, True)
+ 224 Just TypeBlob -> return (perm, name, False)
+ 225 Just _ -> error "unexpected tree entry object type"
+ 226 + 227 -- | Given a tree, resolve the given path in it and return the 'ObjId' found
+ 228 -- (which should be either a Tree or a Blob, i.e. a directory or a file). If
+ 229 -- the path doesn't exist, throw exception. If the path is the empty list, it
+ 230 -- refers to the tree you passed, and 'Nothing' is returned.
+ 231 resolveTreePath :: Git -> Tree -> EntPath -> IO (Maybe ObjId)
+ 232 resolveTreePath _ _ [] = return Nothing
+ 233 resolveTreePath git tree (p:ps) =
+ 234 let match ent (_, name, _) = name == ent
+ 235 go ref [] = return $ Just $ ObjId ref
+ 236 go ref (q:qs) = do
+ 237 t <- getTree git ref
+ 238 case find (match q) $ treeGetEnts t of
+ 239 Nothing -> error "no such path in tree"
+ 240 Just (_, _, r) -> go r qs
+ 241 in case find (match p) $ treeGetEnts tree of
+ 242 Nothing -> error "no such path in the tree"
+ 243 Just (_, _, ref) -> go ref ps
… … … … Add file src/Data/Git/Harder/Pack.hs 0
Edit file src/Data/Git/Harder/Pack.hs 0 → 0
+ 1 {- This file is part of hit-harder.
+ 2 -
+ 3 - Written in 2016 by fr33domlover <fr33domlover@riseup.net>.
+ 4 -
+ 5 - ♡ Copying is an act of love. Please copy, reuse and share.
+ 6 -
+ 7 - The author(s) have dedicated all copyright and related and neighboring
+ 8 - rights to this software to the public domain worldwide. This software is
+ 9 - distributed without any warranty.
+ 10 -
+ 11 - You should have received a copy of the CC0 Public Domain Dedication along
+ 12 - with this software. If not, see
+ 13 - <http://creativecommons.org/publicdomain/zero/1.0/>.
+ 14 -}
+ 15 + 16 module Data.Git.Harder.Pack
+ 17 (
+ 18 )
+ 19 where
+ 20 + 21 import Data.Binary.Put
+ 22 import Data.Bits
+ 23 + 24 putPackHeader :: Int -> Put
+ 25 putPackHeader numOfObjects = do
+ 26 putByteString "PACK" -- Signature
+ 27 putWord32be 2 -- Version number
+ 28 putInt32be numOfObjects -- Number of objects contained in the pack
+ 29 + 30 type ObjIdSet = HashSet ObjId
+ 31 + 32 -- | Take a minimal list of commits we must send, and build a set of object IDs
+ 33 -- of these commits and all the trees and blobs they refer to recursively.
+ 34 collectObjIds :: Git -> [(ObjId, Commit)] -> IO ObjIdSet
+ 35 collectObjIds git pairs = do
+ 36 let (commitIds, commits) = unzip pairs
+ 37 treeIds = map (commitTreeish git) commits
+ 38 resolve tid = do
+ 39 mtree <- resolveTreeish git $ unObjId tid
+ 40 return $ fromMaybe (error "invalid commit treeish ref") mtree
+ 41 visit s oid _ _ _ = S.insert oid s
+ 42 collect = traverseTree git visit
+ 43 trees <- traverse resolve treeIds
+ 44 let initial = S.fromList commitsIds `S.union` S.fromList treeIds
+ 45 foldlM (flip collect) initial trees
+ 46 + 47 data CompressedObject = CompressedObject
+ 48 { zoType :: ObjectType
+ 49 , zoSize :: Word64
+ 50 , zoData :: BL.ByteString
+ 51 }
+ 52 + 53 objectTypeCode :: Num a => ObjectType -> a
+ 54 objectTypeCode TypeCommit = 1
+ 55 objectTypeCode TypeTree = 2
+ 56 objectTypeCode TypeBlob = 3
+ 57 objectTypeCode TypeTag = 4
+ 58 objectTypeCode TypeDeltaOff = 6
+ 59 objectTypeCode TypeDeltaRef = 7
+ 60 + 61 -- | Get the low 7 bits of a number. You get them as the low 7 bits of the
+ 62 -- 'Word8' returned.
+ 63 low7bits :: Bits a => a -> Word8
+ 64 low7bits n =
+ 65 let mw = fromIntegralSized $ n .&. 0x7f
+ 66 msg = "toIntegralSized failed to convert small Bits a (0-127) to Word8"
+ 67 in fromMaybe (error msg) mw
+ 68 + 69 -- | This is an encoder for a specific encoding of arbitrary-length numbers
+ 70 -- used by Git. The purpose is to support objects of arbitrary size, not
+ 71 -- limiting their size representation to 32 or 64 bits.
+ 72 --
+ 73 -- The encoding work as follows. The number is split into sequences of 7 bits,
+ 74 -- in little endian order (i.e. least significant bits come first). For each
+ 75 -- sequence, a byte is constructed. The sequence serve as the low 7 bits of it,
+ 76 -- and the highest bit determines whether there is another sequence after it.
+ 77 -- In other words, the last byte has that bit set to 0, and all other bytes
+ 78 -- have it set to 1.
+ 79 --
+ 80 -- The encoding contains at least one byte. If the word value is 127 or less,
+ 81 -- i.e. can be expressed in 7 bits, the encoding contains a single byte.
+ 82 -- Otherwise, it's more than one byte, as needed.
+ 83 putExtensibleWord :: Bits a => a -> Put
+ 84 putExtensibleWord n =
+ 85 let first = low7bits n
+ 86 rest = unsafeShiftR n 7
+ 87 in putExtensibleWord' first rest
+ 88 + 89 -- | Like 'putExtensibleWord'', but lets you manually pass the first 7 bits
+ 90 -- separately from the rest of the bits.
+ 91 --
+ 92 -- If the rest of the bits are all zeros, te encoding will contain a single
+ 93 -- byte (the first 7 bits passed, and a zero high bit). Otherwise, it will
+ 94 -- contain at least 2 bytes: 1 byte for the first 7 bits, and at least 1 byte
+ 95 -- for the rest of the bits.
+ 96 putExtensibleWord'
+ 97 :: Bits a
+ 98 => Word8 -- ^ The low 7 bits of the word
+ 99 -> a -- ^ The rest of the bits
+ 100 -> Put
+ 101 putExtensibleWord' first rest =
+ 102 let setHigh = (.|. 0x80)
+ 103 clearHigh = (.&. 0x7f)
+ 104 continues = setHigh
+ 105 stops = clearHigh
+ 106 if rest == zeroBits
+ 107 then putWord8 $ stops first
+ 108 else do
+ 109 putWord8 $ continues first
+ 110 let first' = low7bits rest
+ 111 rest' = unsafeShiftR rest 7
+ 112 putExtensibleWord' first' rest'
+ 113 + 114 putObjectHeader :: ObjectType -> Word64 -> Put
+ 115 putObjectHeader otype size =
+ 116 let typeBits = objectTypeCode otype
+ 117 msizeLowBits = toIntegralSized $ size .&. 0x0f
+ 118 sizeLowBits = case msizeLowBits of
+ 119 Nothing ->
+ 120 error
+ 121 "toIntegralSized failed to convert small (0-15) Word64 to \
+ 122 \Word8 in putObjectHeader"
+ 123 Just n -> n
+ 124 first7bits = unsafeShiftL typeBits 4 .|. sizeLowBits
+ 125 in putExtensibleWord' first7bits (unsafeShiftR size 4)
+ 126 + 127 putCompressedObject :: CompressedObject -> Put
+ 128 putCompressedObject zo = do
+ 129 putObjectHeader (zoType zo) (zoSize zo)
+ 130 putLazyByteString $ zoData zo
+ 131 + 132 compressObject :: ObjectInfo -> CompressedObject
+ 133 compressObect (ObjectInfo (t, s, _mp) odata _ochains) = CompressedObject
+ 134 { zoType = t
+ 135 , zoSize = s
+ 136 , zoData = compress odata
+ 137 }
+ 138 + 139 putObject :: ObjectInfo -> Put
+ 140 putObject = putCompressedObject . compressObject
+ 141 + 142 mkPutObject :: Git -> ObjId -> IO Put
+ 143 mkPutObject git oid = do
+ 144 minfo <- getObjectRaw git (unObjId oid) True
+ 145 case minfo of
+ 146 Nothing -> error "failed to load raw object from oid"
+ 147 Just info -> return $ putObject info
+ 148 + 149 writeHashed :: Put -> HashT SHA1 IO Put
+ 150 writeHashed put = do
+ 151 let lbs = runPut put
+ 152 updateHashMulti $ toChunks lbs
+ 153 return $ putLazyByteString lbs
+ 154 + 155 writePack :: Git -> ObjIdSet -> IO Put
+ 156 writePack git oidset = do
+ 157 (put, digest) <- runHashT $ do
+ 158 header <- writeHashed $ putPackHeader $ S.size oidset
+ 159 let writeObj oid = liftIO (mkPutObject git oid) >>= writeHashed
+ 160 foldlM (\ put oid -> (put >>) <$> writeObj oid) header oidset
+ 161 return $ put >> putByteString (convert digest)
… … … … Add file stack.yaml 0
Edit file stack.yaml 0 → 0
+ 1 # For more information, see:
+ 2 # http://docs.haskellstack.org/en/stable/yaml_configuration.html
+ 3 + 4 # Specifies the GHC version and set of packages available (e.g., lts-3.5,
+ 5 # nightly-2015-09-21, ghc-7.10.2)
+ 6 resolver: lts-5.14
+ 7 + 8 # Local packages, usually specified by relative directory name
+ 9 packages:
+ 10 - '.'
+ 11 + 12 # Packages to be pulled from upstream that are not in the resolver (e.g.,
+ 13 # acme-missiles-0.3)
+ 14 extra-deps: []
+ 15 + 16 # Override default flag values for local packages and extra-deps
+ 17 flags: {}
+ 18 + 19 # Extra package databases containing global packages
+ 20 extra-package-dbs: []
+ 21 + 22 # Control whether we use the GHC we find on the path
+ 23 # system-ghc: true
+ 24 + 25 # Require a specific version of stack, using version ranges
+ 26 # require-stack-version: -any # Default
+ 27 # require-stack-version: >= 1.0.0
+ 28 + 29 # Override the architecture used by stack
+ 30 # arch: i386
+ 31 # arch: x86_64
+ 32 + 33 # Extra directories used by stack for building
+ 34 # extra-include-dirs: [/path/to/dir]
+ 35 # extra-lib-dirs: [/path/to/dir]
+ 36 + 37 # Allow a newer minor version of GHC than the snapshot specifies
+ 38 # compiler-check: newer-minor
… … … … Add file Pref 0
+ 1 changepref boringfile _boring