diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+## Changes in 0.20.0
+  - Do not overwrite any existing cabal file if it has been modified manually
+
 ## Changes in 0.19.3
   - Add support for `frameworks` and `extra-frameworks-dirs`
 
diff --git a/hpack.cabal b/hpack.cabal
--- a/hpack.cabal
+++ b/hpack.cabal
@@ -1,9 +1,9 @@
--- This file has been generated from package.yaml by hpack version 0.19.1.
+-- This file has been generated from package.yaml by hpack version 0.19.3.
 --
 -- see: https://github.com/sol/hpack
 
 name:           hpack
-version:        0.19.3
+version:        0.20.0
 synopsis:       An alternative format for Haskell packages
 description:    See README at <https://github.com/sol/hpack#readme>
 category:       Development
@@ -34,6 +34,7 @@
     , base-compat >=0.8
     , bytestring
     , containers
+    , cryptonite
     , deepseq
     , directory
     , filepath
@@ -47,6 +48,7 @@
       Hpack.Run
       Hpack.Yaml
   other-modules:
+      Hpack.CabalFile
       Hpack.Dependency
       Hpack.FormattingHints
       Hpack.GenericsUtil
@@ -70,6 +72,7 @@
     , base-compat >=0.8
     , bytestring
     , containers
+    , cryptonite
     , deepseq
     , directory
     , filepath
@@ -100,6 +103,7 @@
     , base-compat >=0.8
     , bytestring
     , containers
+    , cryptonite
     , deepseq
     , directory
     , filepath
@@ -113,6 +117,7 @@
     , yaml
   other-modules:
       Helper
+      Hpack.CabalFileSpec
       Hpack.ConfigSpec
       Hpack.DependencySpec
       Hpack.FormattingHintsSpec
@@ -124,6 +129,7 @@
       Hpack.UtilSpec
       HpackSpec
       Hpack
+      Hpack.CabalFile
       Hpack.Config
       Hpack.Dependency
       Hpack.FormattingHints
diff --git a/src/Hpack.hs b/src/Hpack.hs
--- a/src/Hpack.hs
+++ b/src/Hpack.hs
@@ -4,12 +4,13 @@
 , hpackResult
 , Result(..)
 , Status(..)
+, Verbose(..)
+, Force(..)
 , version
 , main
 #ifdef TEST
-, hpackWithVersion
-, extractVersion
-, parseVersion
+, header
+, hpackWithVersionResult
 , splitDirectory
 #endif
 ) where
@@ -19,10 +20,6 @@
 
 import           Control.Monad.Compat
 import qualified Data.ByteString as B
-import           Data.List.Compat
-import           Data.Maybe
-import qualified Data.Text as T
-import           Data.Text.Encoding (encodeUtf8)
 import           Data.Version (Version)
 import qualified Data.Version as Version
 import           System.Environment
@@ -30,22 +27,24 @@
 import           System.IO
 import           System.FilePath
 import           System.Directory
-import           Text.ParserCombinators.ReadP
 
 import           Paths_hpack (version)
 import           Hpack.Options
 import           Hpack.Config
 import           Hpack.Run
 import           Hpack.Util
+import           Hpack.CabalFile
 
 programVersion :: Version -> String
 programVersion v = "hpack version " ++ Version.showVersion v
 
-header :: FilePath -> Version -> String
-header p v = unlines [
+header :: FilePath -> Version -> Hash -> String
+header p v hash = unlines [
     "-- This file has been generated from " ++ p ++ " by " ++ programVersion v ++ "."
   , "--"
   , "-- see: https://github.com/sol/hpack"
+  , "--"
+  , "-- hash: " ++ hash
   , ""
   ]
 
@@ -56,8 +55,8 @@
     PrintVersion -> putStrLn (programVersion version)
     Help -> printHelp
     Run options -> case options of
-      Options _verbose True dir -> hpackStdOut dir
-      Options verbose False dir -> hpack dir verbose
+      Options _verbose _force True dir -> hpackStdOut dir
+      Options verbose force False dir -> hpack dir verbose force
     ParseError -> do
       printHelp
       exitFailure
@@ -65,47 +64,40 @@
 printHelp :: IO ()
 printHelp = do
   hPutStrLn stderr $ unlines [
-      "Usage: hpack [ --silent ] [ dir ] [ - ]"
+      "Usage: hpack [ --silent ] [ --force | -f ] [ PATH ] [ - ]"
     , "       hpack --version"
+    , "       hpack --help"
     ]
 
-safeInit :: [a] -> [a]
-safeInit [] = []
-safeInit xs = init xs
-
-extractVersion :: [String] -> Maybe Version
-extractVersion = listToMaybe . mapMaybe (stripPrefix prefix >=> parseVersion . safeInit)
-  where
-    prefix = "-- This file has been generated from package.yaml by hpack version "
-
-parseVersion :: String -> Maybe Version
-parseVersion xs = case [v | (v, "") <- readP_to_S Version.parseVersion xs] of
-  [v] -> Just v
-  _ -> Nothing
-
-hpack :: Maybe FilePath -> Bool -> IO ()
+hpack :: Maybe FilePath -> Verbose -> Force -> IO ()
 hpack = hpackWithVersion version
 
-hpackResult :: Maybe FilePath -> IO Result
+hpackResult :: Maybe FilePath -> Force -> IO Result
 hpackResult = hpackWithVersionResult version
 
 data Result = Result {
   resultWarnings :: [String]
 , resultCabalFile :: String
 , resultStatus :: Status
-}
+} deriving (Eq, Show)
 
-data Status = Generated | AlreadyGeneratedByNewerHpack | OutputUnchanged
+data Status =
+    Generated
+  | ExistingCabalFileWasModifiedManually
+  | AlreadyGeneratedByNewerHpack
+  | OutputUnchanged
+  deriving (Eq, Show)
 
-hpackWithVersion :: Version -> Maybe FilePath -> Bool -> IO ()
-hpackWithVersion v p verbose = do
-    r <- hpackWithVersionResult v p
+hpackWithVersion :: Version -> Maybe FilePath -> Verbose -> Force -> IO ()
+hpackWithVersion v p verbose force = do
+    r <- hpackWithVersionResult v p force
     printWarnings (resultWarnings r)
-    when verbose $ putStrLn $
+    when (verbose == Verbose) $ putStrLn $
       case resultStatus r of
         Generated -> "generated " ++ resultCabalFile r
         OutputUnchanged -> resultCabalFile r ++ " is up-to-date"
         AlreadyGeneratedByNewerHpack -> resultCabalFile r ++ " was generated with a newer version of hpack, please upgrade and try again."
+        ExistingCabalFileWasModifiedManually -> resultCabalFile r ++ " was modified manually, please use --force to overwrite."
 
 printWarnings :: [String] -> IO ()
 printWarnings warnings = do
@@ -122,33 +114,40 @@
       dir = takeDirectory p
       in (guard (p /= file) >> Just dir, if null file then packageConfig else file)
 
-hpackWithVersionResult :: Version -> Maybe FilePath -> IO Result
-hpackWithVersionResult v p = do
+mkStatus :: [String] -> Version -> CabalFile -> Status
+mkStatus new v (CabalFile mOldVersion mHash old) = case (mOldVersion, mHash) of
+  (Nothing, _) -> ExistingCabalFileWasModifiedManually
+  (Just oldVersion, _) | oldVersion < makeVersion [0, 20, 0] -> Generated
+  (_, Nothing) -> ExistingCabalFileWasModifiedManually
+  (Just oldVersion, Just hash)
+    | v < oldVersion -> AlreadyGeneratedByNewerHpack
+    | sha256 (unlines old) /= hash -> ExistingCabalFileWasModifiedManually
+    | old == new -> OutputUnchanged
+    | otherwise -> Generated
+
+hpackWithVersionResult :: Version -> Maybe FilePath -> Force -> IO Result
+hpackWithVersionResult v p force = do
   (dir, file) <- splitDirectory p
   (warnings, cabalFile, new) <- run dir file
-  old <- fmap splitHeader <$> tryReadFile cabalFile
-  let oldVersion = fmap fst old >>= extractVersion
-  status <-
-    if (oldVersion <= Just v) then
-      if (fmap snd old == Just (lines new)) then
-        return OutputUnchanged
-      else do
-        B.writeFile cabalFile $ encodeUtf8 $ T.pack $ header file v ++ new
-        return Generated
-    else
-      return AlreadyGeneratedByNewerHpack
+  oldCabalFile <- readCabalFile cabalFile
+  let
+    status = case force of
+      Force -> Generated
+      NoForce -> maybe Generated (mkStatus (lines new) v) oldCabalFile
+  case status of
+    Generated -> do
+      let hash = sha256 new
+      B.writeFile cabalFile . encodeUtf8 $ header file v hash ++ new
+    _ -> return ()
   return Result
     { resultWarnings = warnings
     , resultCabalFile = cabalFile
     , resultStatus = status
     }
-  where
-    splitHeader :: String -> ([String], [String])
-    splitHeader = fmap (dropWhile null) . span ("--" `isPrefixOf`) . lines
 
 hpackStdOut :: Maybe FilePath -> IO ()
 hpackStdOut p = do
   (dir, file) <- splitDirectory p
   (warnings, _cabalFile, new) <- run dir file
-  B.putStr $ encodeUtf8 $ T.pack new
+  B.putStr (encodeUtf8 new)
   printWarnings warnings
diff --git a/src/Hpack/CabalFile.hs b/src/Hpack/CabalFile.hs
new file mode 100644
--- /dev/null
+++ b/src/Hpack/CabalFile.hs
@@ -0,0 +1,54 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE RecordWildCards #-}
+{-# LANGUAGE ViewPatterns #-}
+module Hpack.CabalFile where
+
+import           Prelude ()
+import           Prelude.Compat
+
+import           Control.Monad.Compat
+import           Data.List.Compat
+import           Data.Maybe
+import           Data.Version (Version(..))
+import qualified Data.Version as Version
+import           Text.ParserCombinators.ReadP
+
+import           Hpack.Util
+
+makeVersion :: [Int] -> Version
+makeVersion v = Version v []
+
+data CabalFile = CabalFile {
+  cabalFileHpackVersion :: Maybe Version
+, cabalFileHash :: Maybe Hash
+, cabalFileContents :: [String]
+} deriving (Eq, Show)
+
+readCabalFile :: FilePath -> IO (Maybe CabalFile)
+readCabalFile cabalFile = fmap parse <$> tryReadFile cabalFile
+  where
+    parse :: String -> CabalFile
+    parse (splitHeader -> (h, c)) = CabalFile (extractVersion h) (extractHash h) c
+
+    splitHeader :: String -> ([String], [String])
+    splitHeader = fmap (dropWhile null) . span ("--" `isPrefixOf`) . lines
+
+extractHash :: [String] -> Maybe Hash
+extractHash = extract "-- hash: " Just
+
+extractVersion :: [String] -> Maybe Version
+extractVersion = extract prefix (parseVersion . safeInit)
+  where
+    prefix = "-- This file has been generated from package.yaml by hpack version "
+
+extract :: String -> (String -> Maybe a) -> [String] -> Maybe a
+extract prefix parse = listToMaybe . mapMaybe (stripPrefix prefix >=> parse)
+
+safeInit :: [a] -> [a]
+safeInit [] = []
+safeInit xs = init xs
+
+parseVersion :: String -> Maybe Version
+parseVersion xs = case [v | (v, "") <- readP_to_S Version.parseVersion xs] of
+  [v] -> Just v
+  _ -> Nothing
diff --git a/src/Hpack/Options.hs b/src/Hpack/Options.hs
--- a/src/Hpack/Options.hs
+++ b/src/Hpack/Options.hs
@@ -6,8 +6,15 @@
 data ParseResult = Help | PrintVersion | Run Options | ParseError
   deriving (Eq, Show)
 
+data Verbose = Verbose | NoVerbose
+  deriving (Eq, Show)
+
+data Force = Force | NoForce
+  deriving (Eq, Show)
+
 data Options = Options {
-  optionsVerbose :: Bool
+  optionsVerbose :: Verbose
+, optionsForce :: Force
 , optionsToStdout :: Bool
 , optionsTarget :: Maybe FilePath
 } deriving (Eq, Show)
@@ -17,12 +24,16 @@
   ["--version"] -> PrintVersion
   ["--help"] -> Help
   _ -> case targets of
-    Just (target, toStdout) -> Run (Options verbose toStdout target)
+    Just (target, toStdout) -> Run (Options verbose force toStdout target)
     Nothing -> ParseError
     where
       silentFlag = "--silent"
-      verbose = not (silentFlag `elem` xs)
-      ys = filter (/= silentFlag) xs
+      forceFlags = ["--force", "-f"]
+
+      flags = [silentFlag] ++ forceFlags
+      verbose = if silentFlag `elem` xs then NoVerbose else Verbose
+      force = if any (`elem` xs) forceFlags then Force else NoForce
+      ys = filter (`notElem` flags) xs
 
       targets = case ys of
         ["-"] -> Just (Nothing, True)
diff --git a/src/Hpack/Util.hs b/src/Hpack/Util.hs
--- a/src/Hpack/Util.hs
+++ b/src/Hpack/Util.hs
@@ -15,6 +15,9 @@
 , expandGlobs
 , sort
 , lexicographically
+, encodeUtf8
+, Hash
+, sha256
 ) where
 
 import           Prelude ()
@@ -29,13 +32,14 @@
 import           Data.List.Compat hiding (sort)
 import           Data.Ord
 import qualified Data.Text as T
-import           Data.Text.Encoding (decodeUtf8With)
+import qualified Data.Text.Encoding as Encoding
 import           Data.Text.Encoding.Error (lenientDecode)
 import           System.IO.Error
 import           System.Directory
 import           System.FilePath
 import qualified System.FilePath.Posix as Posix
 import           System.FilePath.Glob
+import           Crypto.Hash
 
 import           Hpack.Haskell
 
@@ -110,7 +114,7 @@
 tryReadFile :: FilePath -> IO (Maybe String)
 tryReadFile file = do
   r <- tryJust (guard . isDoesNotExistError) (B.readFile file)
-  return $ either (const Nothing) (Just . T.unpack . decodeUtf8With lenientDecode) r
+  return $ either (const Nothing) (Just . T.unpack . Encoding.decodeUtf8With lenientDecode) r
 
 toPosixFilePath :: FilePath -> FilePath
 toPosixFilePath = Posix.joinPath . splitDirectories
@@ -149,3 +153,11 @@
       , pathSepInRanges = False
       , errorRecovery = True
       }
+
+encodeUtf8 :: String -> B.ByteString
+encodeUtf8 = Encoding.encodeUtf8 . T.pack
+
+type Hash = String
+
+sha256 :: String -> Hash
+sha256 c = show (hash (encodeUtf8 c) :: Digest SHA256)
diff --git a/test/Hpack/CabalFileSpec.hs b/test/Hpack/CabalFileSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Hpack/CabalFileSpec.hs
@@ -0,0 +1,38 @@
+module Hpack.CabalFileSpec (spec) where
+
+import           Helper
+import           Test.QuickCheck
+import           Data.Version (showVersion)
+import           Control.Monad.Compat
+
+import           Paths_hpack (version)
+
+import           Hpack (header)
+import           Hpack.CabalFile
+
+spec :: Spec
+spec = do
+  describe "readCabalFile" $ do
+    let
+      file = "package.yaml"
+      hash = "some-hash"
+    it "includes hash" $ do
+      inTempDirectory $ do
+        writeFile file $ header file version hash
+        readCabalFile file `shouldReturn` Just (CabalFile (Just version) (Just hash) [])
+
+  describe "extractVersion" $ do
+    it "extracts Hpack version from a cabal file" $ do
+      let cabalFile = ["-- This file has been generated from package.yaml by hpack version 0.10.0."]
+      extractVersion cabalFile `shouldBe` Just (makeVersion [0, 10, 0])
+
+    it "is total" $ do
+      let cabalFile = ["-- This file has been generated from package.yaml by hpack version "]
+      extractVersion cabalFile `shouldBe` Nothing
+
+  describe "parseVersion" $ do
+    it "is inverse to showVersion" $ do
+      let positive = getPositive <$> arbitrary
+      forAll (replicateM 3 positive) $ \xs -> do
+        let v = makeVersion xs
+        parseVersion (showVersion v) `shouldBe` Just v
diff --git a/test/Hpack/OptionsSpec.hs b/test/Hpack/OptionsSpec.hs
--- a/test/Hpack/OptionsSpec.hs
+++ b/test/Hpack/OptionsSpec.hs
@@ -20,22 +20,30 @@
 
     context "by default" $ do
       it "returns Run" $ do
-        parseOptions [] `shouldBe` Run (Options True False Nothing)
+        parseOptions [] `shouldBe` Run (Options Verbose NoForce False Nothing)
 
       it "includes target" $ do
-        parseOptions ["foo"] `shouldBe` Run (Options True False (Just "foo"))
+        parseOptions ["foo"] `shouldBe` Run (Options Verbose NoForce False (Just "foo"))
 
       context "with superfluous arguments" $ do
         it "returns ParseError" $ do
           parseOptions ["foo", "bar"] `shouldBe` ParseError
 
       context "with --silent" $ do
-        it "sets optionsVerbose to False" $ do
-          parseOptions ["--silent"] `shouldBe` Run (Options False False Nothing)
+        it "sets optionsVerbose to NoVerbose" $ do
+          parseOptions ["--silent"] `shouldBe` Run (Options NoVerbose NoForce False Nothing)
 
+      context "with --force" $ do
+        it "sets optionsForce to Force" $ do
+          parseOptions ["--force"] `shouldBe` Run (Options Verbose Force False Nothing)
+
+      context "with -f" $ do
+        it "sets optionsForce to Force" $ do
+          parseOptions ["-f"] `shouldBe` Run (Options Verbose Force False Nothing)
+
       context "with -" $ do
         it "sets optionsToStdout to True" $ do
-          parseOptions ["-"] `shouldBe` Run (Options True True Nothing)
+          parseOptions ["-"] `shouldBe` Run (Options Verbose NoForce True Nothing)
 
         it "rejects - for target" $ do
           parseOptions ["-", "-"] `shouldBe` ParseError
diff --git a/test/HpackSpec.hs b/test/HpackSpec.hs
--- a/test/HpackSpec.hs
+++ b/test/HpackSpec.hs
@@ -3,64 +3,98 @@
 import           Helper
 
 import           Prelude ()
-import           Prelude.Compat
+import           Prelude.Compat hiding (readFile)
+import qualified Prelude.Compat as Prelude
 
-import           Control.Monad.Compat
 import           Control.DeepSeq
-import           Data.Version (Version(..), showVersion)
 
-import           Test.QuickCheck
-
-import           Hpack
+import           Hpack.Config (packageConfig)
+import           Hpack.CabalFile
+import           Hpack hiding (hpack)
 
-makeVersion :: [Int] -> Version
-makeVersion v = Version v []
+readFile :: FilePath -> IO String
+readFile name = Prelude.readFile name >>= (return $!!)
 
 spec :: Spec
 spec = do
-  describe "extractVersion" $ do
-    it "extracts Hpack version from a cabal file" $ do
-      let cabalFile = ["-- This file has been generated from package.yaml by hpack version 0.10.0."]
-      extractVersion cabalFile `shouldBe` Just (Version [0, 10, 0] [])
+  describe "hpackWithVersionResult" $ do
+    context "with existing cabal file" $ around_ inTempDirectory $ before_ (writeFile packageConfig "name: foo") $ do
+      let
+        file = "foo.cabal"
 
-    it "is total" $ do
-      let cabalFile = ["-- This file has been generated from package.yaml by hpack version "]
-      extractVersion cabalFile `shouldBe` Nothing
+        hpackWithVersion v = hpackWithVersionResult v Nothing NoForce
+        hpack = hpackWithVersionResult version Nothing NoForce
+        hpackForce = hpackWithVersionResult version Nothing Force
 
-  describe "parseVersion" $ do
-    it "is inverse to showVersion" $ do
-      let positive = getPositive <$> arbitrary
-      forAll (replicateM 3 positive) $ \xs -> do
-        let v = Version xs []
-        parseVersion (showVersion v) `shouldBe` Just v
+        generated = Result [] file Generated
+        modifiedManually = Result [] file ExistingCabalFileWasModifiedManually
+        outputUnchanged = Result [] file OutputUnchanged
+        alreadyGeneratedByNewerHpack = Result [] file AlreadyGeneratedByNewerHpack
 
-  describe "hpackWithVersion" $ do
-    context "when only the hpack version in the cabal file header changed" $ do
-      it "does not write a new cabal file" $ do
-        inTempDirectory $ do
-          writeFile "package.yaml" "name: foo"
-          hpackWithVersion (makeVersion [0,8,0]) Nothing False
-          old <- readFile "foo.cabal" >>= (return $!!)
-          hpackWithVersion (makeVersion [0,10,0]) Nothing False
-          readFile "foo.cabal" `shouldReturn` old
+      context "when cabal file was created manually" $ do
+        it "does not overwrite existing cabal file" $ do
+          let existing = "some existing cabal file"
+          writeFile file existing
+          hpack `shouldReturn` modifiedManually
+          readFile file `shouldReturn` existing
 
-    context "when exsting cabal file was generated with a newer version of hpack" $ do
-      it "does not re-generate" $ do
-        inTempDirectory $ do
-          writeFile "package.yaml" $ unlines [
-              "name: foo"
-            , "version: 0.1.0"
-            ]
-          hpackWithVersion (makeVersion [0,10,0]) Nothing False
-          old <- readFile "foo.cabal" >>= (return $!!)
+        context "with --force" $ do
+          it "overwrites existing cabal file" $ do
+            _ <- hpack
+            expected <- readFile file
+            writeFile file "some existing cabal file"
+            hpackForce `shouldReturn` generated
+            readFile file `shouldReturn` expected
 
-          writeFile "package.yaml" $ unlines [
-              "name: foo"
-            , "version: 0.2.0"
-            ]
+      context "when cabal file was created with hpack < 0.20.0" $ do
+        it "overwrites existing cabal file" $ do
+          _ <- hpack
+          expected <- readFile file
+          writeFile file "-- This file has been generated from package.yaml by hpack version 0.19.3."
+          hpack `shouldReturn` generated
+          readFile file `shouldReturn` expected
 
-          hpackWithVersion (makeVersion [0,8,0]) Nothing False
-          readFile "foo.cabal" `shouldReturn` old
+      context "when cabal file was created with hpack >= 0.20.0" $ do
+        context "when hash is missing" $ do
+          it "does not overwrite existing cabal file" $ do
+            let existing = "-- This file has been generated from package.yaml by hpack version 0.20.0."
+            writeFile file existing
+            hpack `shouldReturn` modifiedManually
+            readFile file `shouldReturn` existing
+
+        context "when hash is present" $ do
+          context "when exsting cabal file was generated with a newer version of hpack" $ do
+            it "does not overwrite existing cabal file" $ do
+              writeFile packageConfig $ unlines [
+                  "name: foo"
+                , "version: 0.1.0"
+                ]
+              _ <- hpackWithVersion (makeVersion [0,22,0])
+              old <- readFile file
+
+              writeFile packageConfig $ unlines [
+                  "name: foo"
+                , "version: 0.2.0"
+                ]
+
+              hpackWithVersion (makeVersion [0,20,0]) `shouldReturn` alreadyGeneratedByNewerHpack
+              readFile file `shouldReturn` old
+
+          context "when cabal file was modified manually" $ do
+            it "does not overwrite existing cabal file" $ do
+              _ <- hpack
+              old <- readFile file
+              let modified = old ++ "foo\n"
+              writeFile file modified
+              _ <- hpack
+              readFile file `shouldReturn` modified
+
+          context "when only the hpack version in the cabal file header changed" $ do
+            it "does not overwrite existing cabal file" $ do
+              _ <- hpackWithVersion (makeVersion [0,20,0])
+              old <- readFile file
+              hpack `shouldReturn` outputUnchanged
+              readFile file `shouldReturn` old
 
   describe "splitDirectory" $ do
     context "when given Nothing" $ do
