diff --git a/shift.cabal b/shift.cabal
index 5bcef314a2c5a5a6945eb84f7182fcdd0e367da2..523d573042d51cde5a7a3d25d73b7c248195f670 100644
--- a/shift.cabal
+++ b/shift.cabal
@@ -26,8 +26,8 @@ library
                      , GitHub.UserSearch
   build-depends:       base >= 4.7 && < 5
                      , versions
-                     , hit
                      , text
+                     , hgit
                      , containers
                      , megaparsec
                      , unordered-containers
diff --git a/src/Shift/Git.hs b/src/Shift/Git.hs
index 8780720066fb2f2130b0562dd4ebd6cbfd36926f..1863b6c957da35e7216c32a28c5850148c80d4ac 100644
--- a/src/Shift/Git.hs
+++ b/src/Shift/Git.hs
@@ -1,45 +1,45 @@
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE RankNTypes        #-}
+{-# LANGUAGE RankNTypes #-}
 
 module Shift.Git where
 
-import Control.Monad  (void)
-import Data.Either    (rights)
-import Data.List      (sortBy)
-import Data.Maybe     (catMaybes)
-import Data.Set       as S (toList)
-import Data.Tuple     (swap)
-import System.Process
-
-import           Control.Lens            ((^.))
-import           Control.Monad.Catch     (throwM)
-import           Control.Monad.Reader    (runReaderT)
-import           Control.Monad.State     (runStateT)
-import           Control.Monad.Trans     (liftIO)
-import           Data.ByteString.Char8   (ByteString)
-import qualified Data.ByteString.Char8   as BS
-import           Data.Default            (def)
-import           Data.Git
-import           Data.Git.Ref            (fromHex, isHex)
-import           Data.Git.Storage.Object
-import           Data.String.Conversions (cs)
-import           Data.Text               (Text)
-import qualified Data.Text               as T
-import qualified Data.Text.IO            as TIO
-import           Data.Versions           (parseV)
-import           GitHub.Auth             (Auth (OAuth))
-import           Network.HTTP.Client     (newManager)
-import           Network.HTTP.Client.TLS (tlsManagerSettings)
-import           Text.Megaparsec         (Dec, ParseError)
-
+import Control.Lens ((^.))
+import Control.Monad (void)
+import Control.Monad.Catch (throwM)
+import Control.Monad.Reader (runReaderT)
+import Control.Monad.State (runStateT)
+import Control.Monad.Trans (liftIO)
+import Data.ByteString.Char8 (ByteString)
+import qualified Data.ByteString.Char8 as BS
+import Data.Default (def)
+import Data.Either (rights)
+import Data.Git
+import Data.Git.Ref (HashAlgorithm, fromHex, isHex)
+import Data.Git.Storage.Object
+import Data.List (sortBy)
+import Data.Maybe (catMaybes)
+import Data.Set as S (toList)
+import Data.String.Conversions (cs)
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.IO as TIO
+import Data.Tuple (swap)
+import Data.Versions (versioning)
+import Data.Void (Void)
+import GitHub.Auth (Auth (OAuth))
+import Network.HTTP.Client (newManager)
+import Network.HTTP.Client.TLS (tlsManagerSettings)
 import Shift.CLI
 import Shift.Processing
 import Shift.Rendering
 import Shift.Types
-import Shift.Utilities  (orError, pairs)
+import Shift.Utilities (orError, pairs)
+import System.Process
+import Text.Megaparsec (ParseError)
+import Text.Megaparsec.Error (ParseErrorBundle)
 
-parseTag :: RefName -> Either (ParseError Char Dec) TagRef
-parseTag ref = case parseV . cs . refNameRaw $ ref of
+parseTag :: RefName -> Either (ParseErrorBundle Text Void) TagRef
+parseTag ref = case versioning . cs . refNameRaw $ ref of
   Left e -> Left e
   Right v -> Right (TagRef ref v)
 
@@ -57,28 +57,30 @@ tempMain opts = withRepo ".git" $ \repo -> do
       runReaderT
         (void $ runStateT (mapM_ (renderDiff repo) pairedTags) state)
         opts
-    GitType -> runReaderT
-      (void $ runStateT (mapM_ (renderDiff repo) pairedTags) GitClientState)
-      opts
-
+    GitType ->
+      runReaderT
+        (void $ runStateT (mapM_ (renderDiff repo) pairedTags) GitClientState)
+        opts
   where
     initGitHubState = do
       manager <- newManager tlsManagerSettings
 
       token <- (opts ^. soGitHubToken) `orError` SEMissingGitHubToken
       repositoryOwner <- (opts ^. soGitHubOwner) `orError` SEMissingGitHubOwner
-      repositoryName <- (opts ^. soGitHubRepository)
-        `orError` SEMissingGitHubRepository
-
-      pure GitHubClientState
-        { _gcsCache = def
-        , _gcsAuth = OAuth (cs token)
-        , _gcsManager = manager
-        , _gcsOwner = cs repositoryOwner
-        , _gcsRepository = cs repositoryName
-        }
-
-renderDiff :: ClientState s => Git -> (TagRef, TagRef) -> GitM s ()
+      repositoryName <-
+        (opts ^. soGitHubRepository)
+          `orError` SEMissingGitHubRepository
+
+      pure
+        GitHubClientState
+          { _gcsCache = def,
+            _gcsAuth = OAuth (cs token),
+            _gcsManager = manager,
+            _gcsOwner = cs repositoryOwner,
+            _gcsRepository = cs repositoryName
+          }
+
+renderDiff :: (ClientState s, HashAlgorithm hash) => Git hash -> (TagRef, TagRef) -> GitM s ()
 renderDiff repo (tx, ty) = do
   liftIO . TIO.putStrLn . headerOne $ renderRange tx ty
 
@@ -88,34 +90,35 @@ renderDiff repo (tx, ty) = do
     [] -> throwM SEUnableToComputeDiff
     diff_ -> printReport (generateReport . rights $ parseCommit <$> diff_)
 
-lookupCommitsDiff :: Git -> TagRef -> TagRef -> GitM s [(Ref, Commit)]
+lookupCommitsDiff :: (HashAlgorithm hash) => Git hash -> TagRef -> TagRef -> GitM s [(Ref hash, Commit hash)]
 lookupCommitsDiff repo x y = do
   rawOutput <- liftIO $ readCreateProcess (shell gitCommand) ""
 
-  catMaybes <$> mapM
-    (lookupRawRef repo)
-    (filter (not . T.null) . T.splitOn "\n" . cs $ rawOutput)
-
+  catMaybes
+    <$> mapM
+      (lookupRawRef repo)
+      (filter (not . T.null) . T.splitOn "\n" . cs $ rawOutput)
   where
-    gitCommand = mconcat
-      [ "git rev-list "
-      , refNameRaw . _tRef $ x
-      , "..."
-      , refNameRaw . _tRef $ y
-      ]
-
-lookupRawRef :: Git -> Text -> GitM s (Maybe (Ref, Commit))
-lookupRawRef repo rr
-  = if isHex . cs $ rr
+    gitCommand =
+      mconcat
+        [ "git rev-list ",
+          refNameRaw . _tRef $ x,
+          "...",
+          refNameRaw . _tRef $ y
+        ]
+
+lookupRawRef :: (HashAlgorithm hash) => Git hash -> Text -> GitM s (Maybe (Ref hash, Commit hash))
+lookupRawRef repo rr =
+  if isHex . cs $ rr
     then do
-        let ref = fromHex . cs $ rr
+      let ref = fromHex . cs $ rr
 
-        object <- liftIO $ getObject repo ref True
+      object <- liftIO $ getObject repo ref True
 
-        case object of
-          Just (ObjCommit commit) -> pure $ Just (ref, commit)
-          _ -> pure Nothing
+      case object of
+        Just (ObjCommit commit) -> pure $ Just (ref, commit)
+        _ -> pure Nothing
     else pure Nothing
 
-commitSummary :: Commit -> ByteString
+commitSummary :: Commit hash -> ByteString
 commitSummary = head . BS.split '\n' . commitMessage
diff --git a/src/Shift/Parsers.hs b/src/Shift/Parsers.hs
index d305d4bae4d5331ff0858b62b3211411a1723347..ed47d3511b1ac12b699d92acafa7edb882574ef3 100644
--- a/src/Shift/Parsers.hs
+++ b/src/Shift/Parsers.hs
@@ -1,36 +1,38 @@
 {-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE ViewPatterns      #-}
+{-# LANGUAGE ViewPatterns #-}
 
 module Shift.Parsers where
 
-import           Control.Monad      (void)
-import           Data.List.NonEmpty (NonEmpty ((:|)))
+import Control.Monad (void)
+import Data.HashSet (HashSet, fromList)
+import Data.List.NonEmpty (NonEmpty ((:|)))
 import qualified Data.List.NonEmpty as NE
-
-import Data.HashSet            (HashSet, fromList)
 import Data.String.Conversions (cs)
+import Data.Text (Text)
+import Data.Void (Void)
+import Shift.Types
 import Text.Megaparsec
-import Text.Megaparsec.Text
+import Text.Megaparsec.Char (char, eol, spaceChar, string)
 
-import Shift.Types
+type Parser = Parsec Void Text
 
 commit :: Parser ParsedCommit
-commit
-  = (PCConventional <$> conventionalCommit)
-  <|> try (PCMerge <$> mergeCommit)
-  <|> (PCMisc <$> miscCommit)
+commit =
+  (PCConventional <$> conventionalCommit)
+    <|> try (PCMerge <$> mergeCommit)
+    <|> (PCMisc <$> miscCommit)
 
 spaced :: Parser a -> Parser a
-spaced = (*>) $ many (some spaceChar <|> eol)
+spaced = (*>) $ many (void (some spaceChar) <|> void eol)
 
 oneParserOf :: NE.NonEmpty (Parser a) -> Parser a
 oneParserOf (NE.reverse -> (x :| xs)) = foldl (\acc y -> try y <|> acc) x xs
 
-manyCharsTill :: Parser end -> Parser [Char]
-manyCharsTill = manyTill anyChar
+manyCharsTill :: Parser end -> Parser [Token Text]
+manyCharsTill = manyTill anySingle
 
-someCharsTill :: Parser end -> Parser [Char]
-someCharsTill = someTill anyChar
+someCharsTill :: Parser end -> Parser [Token Text]
+someCharsTill = someTill anySingle
 
 conventionalCommit :: Parser ConventionalCommit
 conventionalCommit = do
@@ -48,21 +50,30 @@ conventionalCommit = do
   cBreakingChanges <- many (try breakingChange)
   cTicketChanges <- many (try ticketChange)
 
-  pure $ ConventionalCommit
-    cType
-    (cs cScope)
-    (cs cSubject)
-    (cs cBody)
-    cBreakingChanges
-    cTicketChanges
+  pure $
+    ConventionalCommit
+      cType
+      (cs cScope)
+      (cs cSubject)
+      (cs cBody)
+      cBreakingChanges
+      cTicketChanges
 
 commitType :: Parser CommitType
 commitType = do
-  cType <- oneParserOf . fmap string $
-    "feat" :|
-      [ "fix", "bug", "docs", "doc", "style", "refactor", "ref", "test"
-      , "chore"
-      ]
+  cType <-
+    oneParserOf . fmap string $
+      "feat"
+        :| [ "fix",
+             "bug",
+             "docs",
+             "doc",
+             "style",
+             "refactor",
+             "ref",
+             "test",
+             "chore"
+           ]
 
   pure $ case cType of
     "feat" -> CTFeature
@@ -88,13 +99,14 @@ breakingChange = do
 ticketChange :: Parser (HashSet TicketChange)
 ticketChange = do
   tcAction <- spaced $ manyCharsTill (string ": ")
-  tcTickets <- some (char '#' *> manyCharsTill (some spaceChar <|> eol))
+  tcTickets <- some (char '#' *> manyCharsTill (void (some spaceChar) <|> void eol))
 
   pure . fromList $ (\x -> (cs tcAction, cs x)) <$> tcTickets
 
 mergeCommit :: Parser MergeCommit
-mergeCommit = MergeCommit . cs
-  <$> (string "Merge " *> manyCharsTill (skipSome eol <|> eof))
+mergeCommit =
+  MergeCommit . cs
+    <$> (string "Merge " *> manyCharsTill (skipSome eol <|> eof))
 
 miscCommit :: Parser MiscCommit
 miscCommit = MiscCommit . cs <$> manyCharsTill (skipSome eol <|> eof)
diff --git a/src/Shift/Processing.hs b/src/Shift/Processing.hs
index cd3036e2c7e374f0bd666b5066b76f81eeac9e84..2a552c6558c9c022ad8d7f5d986f3341416cc896 100644
--- a/src/Shift/Processing.hs
+++ b/src/Shift/Processing.hs
@@ -1,31 +1,36 @@
 module Shift.Processing where
 
-import Control.Lens            ((<>~), (^.))
-import Data.Default            (def)
-import Data.Git                (Commit, Ref, commitMessage)
+import Control.Lens ((<>~), (^.))
+import Data.Default (def)
+import Data.Git (Commit, Ref, commitMessage)
 import Data.String.Conversions (cs)
-import Text.Megaparsec         (Dec, ParseError, runParser)
-
+import Data.Text (Text)
+import Data.Void (Void)
 import qualified Shift.Parsers as P
-import           Shift.Types
+import Shift.Types
+import Text.Megaparsec (ParseError, ParseErrorBundle, runParser)
 
-parseCommit :: (Ref, Commit) -> Either (ParseError Char Dec) ParsedGroup
+parseCommit :: (Ref hash, Commit hash) -> Either (ParseErrorBundle Text Void) (ParsedGroup hash)
 parseCommit (r, c) = do
   pc <- runParser P.commit "git" . cs . commitMessage $ c
 
   pure (r, c, pc)
 
-generateReport :: [ParsedGroup] -> ChangeReport
+generateReport :: [ParsedGroup hash] -> ChangeReport hash
 generateReport = foldl inner def
   where
     inner acc (r, c, x) = case x of
       PCMisc miscCommit -> crMisc <>~ [(r, c, miscCommit)] $ acc
       PCMerge mergeCommit -> crMerges <>~ [(r, c, mergeCommit)] $ acc
-      PCConventional commit -> (case commit ^. ccType of
-        CTFeature -> crFeatures
-        CTFix -> crFixes
-        CTDocs -> crDocs
-        CTStyle -> crStyles
-        CTRefactor -> crRefactors
-        CTChore -> crChores
-        CTTest -> crTests) <>~ [(r, c, commit)] $ acc
+      PCConventional commit ->
+        ( case commit ^. ccType of
+            CTFeature -> crFeatures
+            CTFix -> crFixes
+            CTDocs -> crDocs
+            CTStyle -> crStyles
+            CTRefactor -> crRefactors
+            CTChore -> crChores
+            CTTest -> crTests
+        )
+          <>~ [(r, c, commit)]
+          $ acc
diff --git a/src/Shift/Rendering.hs b/src/Shift/Rendering.hs
index 27f382b98d0c8e9bb237541d8c255a6cc384f923..1242ad215ecea749fe8465b42d64ad3c2d2ab5dd 100644
--- a/src/Shift/Rendering.hs
+++ b/src/Shift/Rendering.hs
@@ -21,7 +21,7 @@ import Shift.Types
 
 renderRef
   :: (MonadIO m, MonadState s m, ClientState s, MonadThrow m)
-  => Ref
+  => Ref hash
   -> m Text
 renderRef ref = do
   url <- getRefURL ref
@@ -34,7 +34,7 @@ renderRef ref = do
 
 renderConventionalCommit
   :: (MonadIO m, MonadState s m, ClientState s, MonadThrow m)
-  => ConventionalGroup
+  => ConventionalGroup hash
   -> m Text
 renderConventionalCommit (ref, commit, pc) = do
   renderedRef <- renderRef ref
@@ -52,8 +52,8 @@ renderConventionalCommit (ref, commit, pc) = do
 
 renderAuthor
   :: (MonadIO m, MonadState s m, ClientState s, MonadThrow m)
-  => Commit
-  -> Ref
+  => Commit hash
+  -> Ref hash
   -> m Text
 renderAuthor commit ref = do
   authorInfo <- getAuthorInfo (commitAuthor commit) ref
@@ -64,7 +64,7 @@ renderAuthor commit ref = do
 
 renderMiscCommit
   :: (MonadIO m, MonadState s m, ClientState s, MonadThrow m)
-  => MiscGroup
+  => MiscGroup hash
   -> m Text
 renderMiscCommit (ref, commit, MiscCommit subject) = do
   renderedRef <- renderRef ref
@@ -74,7 +74,7 @@ renderMiscCommit (ref, commit, MiscCommit subject) = do
 
 renderMergeCommit
   :: (MonadIO m, MonadState s m, ClientState s, MonadThrow m)
-  => MergeGroup
+  => MergeGroup hash
   -> m Text
 renderMergeCommit (ref, commit, MergeCommit subject) = do
   renderedRef <- renderRef ref
@@ -109,7 +109,7 @@ headerThree = linePadded . (<>) "### "
 
 printReport
   :: (MonadIO m, MonadState s m, ClientState s, MonadThrow m)
-  => ChangeReport
+  => ChangeReport hash
   -> m ()
 printReport report = do
   -- Print conventional commits
diff --git a/src/Shift/Types.hs b/src/Shift/Types.hs
index 047ddfa40faf7e08fded594d6c124eeb13d09378..c31b0aaccd038677ea9c74660dde0393b932f624 100644
--- a/src/Shift/Types.hs
+++ b/src/Shift/Types.hs
@@ -38,16 +38,22 @@ import           Network.HTTP.Client            (Manager)
 import GitHub.UserSearch
 import Shift.CLI         (ShiftOptions)
 import Shift.Utilities   (orThrow)
+import GitHub (URL(URL))
+import GitHub (URL(URL))
+import GitHub (getUrl)
+import GitHub (getUrl)
+import Data.Aeson (FromJSON)
+import Data.Aeson (FromJSON)
 
 class ClientState s where
   getRefURL
     :: (MonadIO m, MonadState s m, MonadThrow m)
-    => Ref
+    => Ref hash
     -> m (Maybe Text)
   getAuthorInfo
     :: (MonadIO m, MonadState s m, MonadThrow m)
     => Person
-    -> Ref
+    -> Ref hash
     -> m (Maybe (Text, Text))
 
 type GitM s a = StateT s (ReaderT ShiftOptions IO) a
@@ -106,26 +112,26 @@ data ParsedCommit
   | PCMisc MiscCommit
   deriving (Show)
 
-type ParsedGroup = (Ref, Commit, ParsedCommit)
-type ConventionalGroup = (Ref, Commit, ConventionalCommit)
-type MergeGroup = (Ref, Commit, MergeCommit)
-type MiscGroup = (Ref, Commit, MiscCommit)
-
-data ChangeReport = ChangeReport
-  { _crFeatures        :: [ConventionalGroup]
-  , _crFixes           :: [ConventionalGroup]
-  , _crDocs            :: [ConventionalGroup]
-  , _crStyles          :: [ConventionalGroup]
-  , _crRefactors       :: [ConventionalGroup]
-  , _crTests           :: [ConventionalGroup]
-  , _crChores          :: [ConventionalGroup]
-  , _crMerges          :: [MergeGroup]
-  , _crMisc            :: [MiscGroup]
+type ParsedGroup hash = (Ref hash, Commit hash, ParsedCommit)
+type ConventionalGroup hash = (Ref hash, Commit hash, ConventionalCommit)
+type MergeGroup hash = (Ref hash, Commit hash, MergeCommit)
+type MiscGroup hash = (Ref hash, Commit hash, MiscCommit)
+
+data ChangeReport hash = ChangeReport
+  { _crFeatures        :: [ConventionalGroup hash]
+  , _crFixes           :: [ConventionalGroup hash]
+  , _crDocs            :: [ConventionalGroup hash]
+  , _crStyles          :: [ConventionalGroup hash]
+  , _crRefactors       :: [ConventionalGroup hash]
+  , _crTests           :: [ConventionalGroup hash]
+  , _crChores          :: [ConventionalGroup hash]
+  , _crMerges          :: [MergeGroup hash]
+  , _crMisc            :: [MiscGroup hash]
   , _crBreakingChanges :: [BreakingChange]
   , _crAffectedTickets :: HashSet TicketChange
   } deriving (Show)
 
-instance Default ChangeReport where
+instance Default (ChangeReport hash) where
   def = ChangeReport
     { _crFeatures = []
     , _crFixes = []
@@ -213,7 +219,7 @@ lookupUserOnGitHubCommit ref = do
 
   pure $ case commitAuthor result of
     Nothing -> Nothing
-    Just user -> Just (untagName $ simpleUserLogin user, simpleUserUrl user)
+    Just user -> Just (untagName $ simpleUserLogin user, getUrl $ simpleUserUrl user)
 
 lookupUserOnGitHub
   :: (MonadIO m, MonadState GitHubClientState m, MonadThrow m)
@@ -230,7 +236,7 @@ lookupUserOnGitHub email = do
     False -> pure Nothing
 
 executeRequest_
-  :: (MonadIO m, MonadState GitHubClientState m, MonadThrow m)
+  :: (MonadIO m, MonadState GitHubClientState m, MonadThrow m, FromJSON a)
   => Request k a
   -> m a
 executeRequest_ x = do
diff --git a/stack.yaml b/stack.yaml
index 2e48a0c3a138ceb447e9d3a44d401ffa4d135ca5..453158da0df599346fd8618f89cfd044af53aec9 100644
--- a/stack.yaml
+++ b/stack.yaml
@@ -2,7 +2,7 @@
 # For more information, see: http://docs.haskellstack.org/en/stable/yaml_configuration/
 
 # Specifies the GHC version and set of packages available (e.g., lts-3.5, nightly-2015-09-21, ghc-7.10.2)
-resolver: lts-7.0
+resolver: nightly-2020-12-12
 
 # Local packages, usually specified by relative directory name
 packages:
@@ -12,8 +12,8 @@ allow-newer: true
 
 # Packages to be pulled from upstream that are not in the resolver (e.g., acme-missiles-0.3)
 extra-deps:
-- github-0.14.1
-- versions-3.0.0
+  - git: https://gitlab.chromabits.com/etcinit/hgit.git
+    commit: 4f39d0dff5cb96995b3a49f2d115caab3da246be
 
 # Override default flag values for local packages and extra-deps
 flags: {}
@@ -21,11 +21,11 @@ flags: {}
 # Extra package databases containing global packages
 extra-package-dbs: []
 
-image:
-  container:
-    base: "fpco/ubuntu-with-libgmp:14.04"
-    entrypoints:
-      - shift
+# image:
+#   container:
+#     base: "fpco/ubuntu-with-libgmp:14.04"
+#     entrypoints:
+#       - shift
 
 # Control whether we use the GHC we find on the path
 # system-ghc: true
diff --git a/stack.yaml.lock b/stack.yaml.lock
new file mode 100644
index 0000000000000000000000000000000000000000..ed1197830f082a7034736a15e76bf4afc6ba0423
--- /dev/null
+++ b/stack.yaml.lock
@@ -0,0 +1,23 @@
+# This file was autogenerated by Stack.
+# You should not edit this file by hand.
+# For more information, please see the documentation at:
+#   https://docs.haskellstack.org/en/stable/lock_files
+
+packages:
+- completed:
+    name: hgit
+    version: 0.4.0
+    git: https://gitlab.chromabits.com/etcinit/hgit.git
+    pantry-tree:
+      size: 2382
+      sha256: 674d4d24286e3a1b80209899056ad1eda2574c5d3c1782935d47af5d890b31c4
+    commit: 4f39d0dff5cb96995b3a49f2d115caab3da246be
+  original:
+    git: https://gitlab.chromabits.com/etcinit/hgit.git
+    commit: 4f39d0dff5cb96995b3a49f2d115caab3da246be
+snapshots:
+- completed:
+    size: 556770
+    url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/2020/12/12.yaml
+    sha256: 168062744e171ba6949149ee702483d2186ddfbb6038508576d6c5093245f534
+  original: nightly-2020-12-12