diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Jiri Marsicek (c) 2015
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * 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.
+
+    * Neither the name of Jiri Marsicek nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
+OWNER 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.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,50 @@
+# merge-bash-history
+
+Utility to be used with unison (http://www.cis.upenn.edu/~bcpierce/unison/) to merge `.bash_history` file accross multiple machines.
+
+# Installation
+
+Clone the repository
+
+```
+git clone https://github.com/j1r1k/merge-bash-history.git
+```
+
+Install using stack
+```
+cd merge-bash-history
+stack install
+```
+
+# Example configuration in unison preferences file
+
+Assuming you have root set to your home directory and `merge-bash-history` is in your $PATH
+
+Add following to your unsion .prf file
+
+```
+merge = Name .bash_history -> merge-bash-history CURRENT1 CURRENT2 > NEW
+backupcurrent = Name .bash_history
+```
+
+# Manual usage
+
+```
+merge-bash-history [INPUT-FILE1] [INPUT-FILE2] > [MERGED-FILE]
+```
+
+# Limitations
+- Does not support line deletion (e.g. manual removal of lines from .bash_history file)
+
+# Warnings
+- Sorts .bash_history file by timestamp if not sorted
+- removes duplicates (comparing timestamp and command)
+
+# Recommended Bash settings
+
+```
+shopt -s histappend
+PROMPT_COMMAND="${PROMPT_COMMAND};history -a"
+```
+
+From Pawel Hajdan (http://phajdan-jr.blogspot.de/2015/03/more-reliable-handling-of-bash-history.html)
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,35 @@
+module Main where
+
+import MergeBashHistory
+
+import Data.Text.IO as TIO (readFile, putStr)
+
+import Options.Applicative
+
+data HmbhO = HmbhO FilePath FilePath
+
+opParseFilePath :: Parser FilePath
+opParseFilePath = argument str $ metavar "FILE"
+
+opParseHmbhO :: Parser HmbhO
+opParseHmbhO = HmbhO <$> opParseFilePath
+                     <*> opParseFilePath
+
+execOpParserHmbhO :: IO HmbhO
+execOpParserHmbhO = execParser $ info (helper <*> opParseHmbhO) (fullDesc <> header "bash_history merger")
+
+handleFile :: FilePath -> IO [HistoryRecord]
+handleFile fp =
+  do i <- TIO.readFile fp
+     case parseFile i of Left e -> error $ show e
+                         Right r -> return r
+
+hmbhHandler :: HmbhO -> IO ()
+hmbhHandler (HmbhO fp1 fp2) =
+  do f1 <- handleFile fp1
+     f2 <- handleFile fp2
+     mapM_ (TIO.putStr . recordToText) $ mergeRecords f1 f2
+
+
+main :: IO ()
+main = execOpParserHmbhO >>= hmbhHandler
diff --git a/merge-bash-history.cabal b/merge-bash-history.cabal
new file mode 100644
--- /dev/null
+++ b/merge-bash-history.cabal
@@ -0,0 +1,49 @@
+name:                merge-bash-history
+version:             0.1.0.0
+synopsis:            Initial project template from stack
+description:         Please see README.md
+homepage:            http://github.com/j1r1k/merge-bash-history#readme
+license:             BSD3
+license-file:        LICENSE
+author:              Jiri Marsicek
+maintainer:          jiri.marsicek@gmail.com
+copyright:           2015 Jiri Marsicek
+category:            Tools
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+
+library
+  hs-source-dirs:      src
+  exposed-modules:     MergeBashHistory
+  build-depends:       base >= 4.7 && < 5
+                     , attoparsec >= 0.12
+                     , data-ordlist >= 0.4
+                     , errors >= 2
+                     , text >= 1.2
+  ghc-options:         -Wall
+  default-language:    Haskell2010
+
+executable merge-bash-history
+  hs-source-dirs:      app
+  main-is:             Main.hs
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  build-depends:       base
+                     , merge-bash-history
+                     , optparse-applicative >=0.11
+                     , text >= 1.2
+  ghc-options:         -Wall
+  default-language:    Haskell2010
+
+test-suite MergeBashHistory-test
+  type:                exitcode-stdio-1.0
+  hs-source-dirs:      test
+  main-is:             Spec.hs
+  build-depends:       base
+                     , merge-bash-history
+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
+  default-language:    Haskell2010
+
+source-repository head
+  type:     git
+  location: https://github.com/j1r1k/merge-bash-history
diff --git a/src/MergeBashHistory.hs b/src/MergeBashHistory.hs
new file mode 100644
--- /dev/null
+++ b/src/MergeBashHistory.hs
@@ -0,0 +1,84 @@
+module MergeBashHistory
+  ( Timestamp
+  , Command
+  , HmbhError
+  , HistoryRecord
+  , mergeRecords
+  , parseFile
+  , recordToText
+  ) where
+
+import Control.Applicative ((<|>))
+
+import Data.EitherR (fmapL)
+import Data.List.Ordered (merge, nub)
+import Data.Text (Text)
+import qualified Data.Text as Text
+
+import Data.Attoparsec.Text
+
+
+type Timestamp = Integer
+type Command = Text
+
+data HistoryLine = Header Timestamp
+                 | Command Command
+                 deriving (Show)
+
+data HmbhError = ParseError String
+               | EmptyFile
+               | MissingHeader
+
+instance Show HmbhError where
+  show (ParseError e) = "Error: Parser Error '" ++ e ++ "'"
+  show EmptyFile      = "Error: Merging with empty file"
+  show MissingHeader  = "Error: Missing header"
+
+data HistoryRecord = HistoryRecord { header   :: Timestamp
+                                   , commands :: [Command]
+                                   } deriving (Eq, Show)
+
+instance Ord HistoryRecord where
+  h1 `compare` h2 = header h1 `compare` header h2
+
+recordToText :: HistoryRecord -> Text
+recordToText (HistoryRecord t c) = Text.unlines $ Text.pack ('#' : show t) : c
+
+emptyRecord :: Timestamp -> HistoryRecord
+emptyRecord t = HistoryRecord { header = t, commands = [] }
+
+appendCommand :: HistoryRecord -> Command -> HistoryRecord
+appendCommand hr c = hr { commands = commands hr ++ [c] }
+
+linesToRecords :: [HistoryLine] -> Either HmbhError [HistoryRecord]
+linesToRecords []             = Left EmptyFile
+linesToRecords (Command _: _) = Left MissingHeader
+linesToRecords (Header t: as) = Right $ squash $ foldr f (emptyRecord t, []) as
+  where f (Header  s) acc           = (emptyRecord s, squash acc)
+        f (Command c) (current, ps) = (appendCommand current c, ps)
+        squash (x, xs) = x : xs
+
+historyHeader :: Parser HistoryLine
+historyHeader =
+  do _ <- char '#'
+     t <- decimal
+     endOfLine
+     return $ Header t
+
+historyCommand :: Parser HistoryLine
+historyCommand =
+  do c <- takeTill isEndOfLine
+     endOfLine
+     return $ Command c
+
+historyLine :: Parser HistoryLine
+historyLine = historyHeader <|> historyCommand
+
+historyLines :: Parser [HistoryLine]
+historyLines = many' historyLine
+
+mergeRecords :: [HistoryRecord] -> [HistoryRecord] -> [HistoryRecord]
+mergeRecords a b = nub $ merge a b
+
+parseFile :: Text -> Either HmbhError [HistoryRecord]
+parseFile t = fmapL ParseError (parseOnly historyLines t) >>= linesToRecords
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,2 @@
+main :: IO ()
+main = putStrLn "Test suite not yet implemented"
