diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Revision history for nix-thunk
 
+## 0.3.0.0
+
+* Fix readThunk when thunk is checked out [#4](https://github.com/obsidiansystems/nix-thunk/pull/4)
+* Fix removal of .git from default destination [#10](https://github.com/obsidiansystems/nix-thunk/pull/10)
+
 ## 0.2.0.3
 
 * Default to GHC 8.8.4 and update dependency bounds
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -65,6 +65,14 @@
   };
 ```
 
+You can also represent in nix all the thunks of a given directory
+```nix
+let sources = nix-thunk.mapSubdirectories nix-thunk.thunkSource ./dep;
+```
+```nix
+{ which = self.callCabal2nix "which" sources.which {}; }
+```
+
 ## Contributing
 Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. See the [contribution guide](CONTRIBUTING.md) for more details.
 
diff --git a/nix-thunk.cabal b/nix-thunk.cabal
--- a/nix-thunk.cabal
+++ b/nix-thunk.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               nix-thunk
-version:            0.2.0.3
+version:            0.3.0.0
 license:            BSD3
 license-file:       LICENSE
 copyright:          Obsidian Systems LLC 2020
diff --git a/src/Nix/Thunk.hs b/src/Nix/Thunk.hs
--- a/src/Nix/Thunk.hs
+++ b/src/Nix/Thunk.hs
@@ -73,11 +73,9 @@
 import Data.Data (Data)
 import Data.Default
 import Data.Either.Combinators (fromRight', rightToMaybe)
-import Data.Foldable (toList)
+import Data.Foldable (for_, toList)
 import Data.Function
-import Data.Functor ((<&>))
 import qualified Data.List as L
-import Data.List (stripPrefix)
 import Data.List.NonEmpty (NonEmpty(..), nonEmpty)
 import qualified Data.List.NonEmpty as NonEmpty
 import Data.Map (Map)
@@ -326,35 +324,38 @@
   -> Set FilePath -- ^ Set of file paths relative to the given directory
   -> m ThunkData
 matchThunkSpecToDir thunkSpec dir dirFiles = do
-  case nonEmpty (toList $ dirFiles `Set.difference` expectedPaths) of
-    Just fs -> throwError $ ReadThunkError_UnrecognizedPaths $ (dir </>) <$> fs
-    Nothing -> pure ()
-  case nonEmpty (toList $ requiredPaths `Set.difference` dirFiles) of
-    Just fs -> throwError $ ReadThunkError_MissingPaths $ (dir </>) <$> fs
-    Nothing -> pure ()
-  datas <- fmap toList $ flip Map.traverseMaybeWithKey (_thunkSpec_files thunkSpec) $ \expectedPath -> \case
-    ThunkFileSpec_AttrCache -> Nothing <$ dirMayExist expectedPath
-    ThunkFileSpec_CheckoutIndicator -> liftIO (doesDirectoryExist (dir </> expectedPath)) <&> \case
-      False -> Nothing
-      True -> Just ThunkData_Checkout
-    ThunkFileSpec_FileMatches expectedContents -> handle (\(e :: IOError) -> throwError $ ReadThunkError_FileError e) $ do
-      actualContents <- liftIO (T.readFile $ dir </> expectedPath)
-      case T.strip expectedContents == T.strip actualContents of
-        True -> pure Nothing
-        False -> throwError $ ReadThunkError_FileDoesNotMatch (dir </> expectedPath) expectedContents
-    ThunkFileSpec_Ptr parser -> handle (\(e :: IOError) -> throwError $ ReadThunkError_FileError e) $ do
-      let path = dir </> expectedPath
-      liftIO (doesFileExist path) >>= \case
-        False -> pure Nothing
-        True -> do
-          actualContents <- liftIO $ LBS.readFile path
-          case parser actualContents of
-            Right v -> pure $ Just (ThunkData_Packed thunkSpec v)
-            Left e -> throwError $ ReadThunkError_UnparseablePtr (dir </> expectedPath) e
+  isCheckout <- fmap or $ flip Map.traverseWithKey (_thunkSpec_files thunkSpec) $ \expectedPath -> \case
+    ThunkFileSpec_CheckoutIndicator -> liftIO (doesDirectoryExist (dir </> expectedPath))
+    _ -> pure False
+  case isCheckout of
+    True -> pure ThunkData_Checkout
+    False -> do
+      for_ (nonEmpty (toList $ dirFiles `Set.difference` expectedPaths)) $ \fs ->
+        throwError $ ReadThunkError_UnrecognizedPaths $ (dir </>) <$> fs
+      for_ (nonEmpty (toList $ requiredPaths `Set.difference` dirFiles)) $ \fs ->
+        throwError $ ReadThunkError_MissingPaths $ (dir </>) <$> fs
+      datas <- fmap toList $ flip Map.traverseMaybeWithKey (_thunkSpec_files thunkSpec) $ \expectedPath -> \case
+        ThunkFileSpec_AttrCache -> Nothing <$ dirMayExist expectedPath
+        ThunkFileSpec_CheckoutIndicator -> pure Nothing -- Handled above
+        ThunkFileSpec_FileMatches expectedContents -> handle (\(e :: IOError) -> throwError $ ReadThunkError_FileError e) $ do
+          actualContents <- liftIO (T.readFile $ dir </> expectedPath)
+          case T.strip expectedContents == T.strip actualContents of
+            True -> pure Nothing
+            False -> throwError $ ReadThunkError_FileDoesNotMatch (dir </> expectedPath) expectedContents
+        ThunkFileSpec_Ptr parser -> handle (\(e :: IOError) -> throwError $ ReadThunkError_FileError e) $ do
+          let path = dir </> expectedPath
+          liftIO (doesFileExist path) >>= \case
+            False -> pure Nothing
+            True -> do
+              actualContents <- liftIO $ LBS.readFile path
+              case parser actualContents of
+                Right v -> pure $ Just (thunkSpec, v)
+                Left e -> throwError $ ReadThunkError_UnparseablePtr (dir </> expectedPath) e
 
-  case nonEmpty datas of
-    Nothing -> throwError ReadThunkError_UnrecognizedThunk
-    Just xs -> fold1WithM xs $ \a b -> either throwError pure (mergeThunkData a b)
+      uncurry ThunkData_Packed <$> case nonEmpty datas of
+        Nothing -> throwError ReadThunkError_UnrecognizedThunk
+        Just xs -> fold1WithM xs $ \a@(_, ptrA) (_, ptrB) ->
+          if ptrA == ptrB then pure a else throwError $ ReadThunkError_AmbiguousPackedState ptrA ptrB
   where
     rootPathsOnly = Set.fromList . mapMaybe takeRootDir . Map.keys
     takeRootDir = fmap NonEmpty.head . nonEmpty . splitPath
@@ -370,15 +371,6 @@
       True -> throwError $ ReadThunkError_UnrecognizedPaths $ expectedPath :| []
       False -> pure ()
 
-    -- Combine 'ThunkData' from different files, preferring "Checkout" over "Packed"
-    mergeThunkData ThunkData_Checkout ThunkData_Checkout = Right ThunkData_Checkout
-    mergeThunkData ThunkData_Checkout ThunkData_Packed{} = Left bothPackedAndUnpacked
-    mergeThunkData ThunkData_Packed{} ThunkData_Checkout = Left bothPackedAndUnpacked
-    mergeThunkData a@(ThunkData_Packed _ ptrA) (ThunkData_Packed _ ptrB) =
-      if ptrA == ptrB then Right a else Left $ ReadThunkError_AmbiguousPackedState ptrA ptrB
-
-    bothPackedAndUnpacked = ReadThunkError_UnrecognizedState "Both packed data and checkout present"
-
     fold1WithM (x :| xs) f = foldM f x xs
 
 readThunkWith
@@ -393,8 +385,7 @@
       Left e -> putLog Debug [i|Thunk specification ${_thunkSpec_name spec} did not match ${dir}: ${e}|] *> loop rest
       x@(Right _) -> x <$ putLog Debug [i|Thunk specification ${_thunkSpec_name spec} matched ${dir}|]
 
--- | Read a thunk and validate that it is exactly a packed thunk.
--- If additional data is present, fail.
+-- | Read a packed or unpacked thunk based on predefined thunk specifications.
 readThunk :: (MonadNixThunk m) => FilePath -> m (Either ReadThunkError ThunkData)
 readThunk = readThunkWith thunkSpecTypes
 
@@ -506,9 +497,8 @@
     (untagName <$> _thunkCreateConfig_branch config)
     (T.pack . show <$> _thunkCreateConfig_rev config)
   let trailingDirectoryName = reverse . takeWhile (/= '/') . dropWhile (=='/') . reverse
-      stripSuffix s = fmap reverse . stripPrefix s . reverse
       dropDotGit :: FilePath -> FilePath
-      dropDotGit origName = fromMaybe origName $ stripSuffix ".git" origName
+      dropDotGit origName = fromMaybe origName $ stripExtension "git" origName
       defaultDestinationForGitUri :: GitUri -> FilePath
       defaultDestinationForGitUri = dropDotGit . trailingDirectoryName . T.unpack . URI.render . unGitUri
       destination = fromMaybe (defaultDestinationForGitUri $ _thunkCreateConfig_uri config) $ _thunkCreateConfig_destination config
