diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright (c) 2015, Eyal Lotem
+
+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 Eyal Lotem 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,39 @@
+# git-jump
+
+## Moving to a new position
+
+When you decide that the current branch needs to use a different hash
+and content, currently you need to `reset --hard` to the new position.
+
+However, `reset --hard` has the annoying side-effect of deleting any
+outstanding work.
+
+It would be nice to be able to jump the tip of the current branch to a
+new position and update the working tree, as long as there's no
+overlap between files changed in the working tree and in the diff to
+the remote location.
+
+Just type:
+
+```
+git-jump <refspec>
+```
+
+To jump to the new location.
+
+## Updating a local tracking branch with no local commits
+
+When you have a local branch that is tracking a remote branch (and has
+no local commits) that was rebased, you would have to `reset --hard`
+to the remote location. This requires typing out the name of the
+upstream branch (or "@{u}" at least), and as usual, loses any
+uncommitted changes.
+
+Just type:
+
+```
+git-jump
+```
+
+With no parameters, and it will jump to the upstream location, taking
+any uncommitted changes on top.
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/git-jump.cabal b/git-jump.cabal
new file mode 100644
--- /dev/null
+++ b/git-jump.cabal
@@ -0,0 +1,31 @@
+-- Initial git-jump.cabal generated by cabal init.  For further
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+name:                git-jump
+version:             0.1.0.0
+synopsis:            Move a git branch
+description:         Move a git branch to a specified refspec (or to the upstream position)
+                     Along with any uncommited changes.
+                     Foregoing any committed changes.  Meant to be a
+                     safer replacement for `reset --hard` to jump
+                     branch tips.
+homepage:            https://github.com/Peaker/git-jump
+license:             BSD3
+license-file:        LICENSE
+author:              Eyal Lotem
+copyright:           Eyal Lotem
+maintainer:          eyal.lotem@gmail.com
+category:            Development
+build-type:          Simple
+extra-source-files:  README.md
+cabal-version:       >=1.10
+source-repository head
+  type: git
+  location: https://github.com/Peaker/git-jump
+
+executable git-jump
+  main-is:             git-jump.hs
+  build-depends:       base >=4.8 && <4.9
+                     , process >=1.0.1.1
+                     , base-compat >=0.8 && <0.9
+  default-language:    Haskell2010
diff --git a/git-jump.hs b/git-jump.hs
new file mode 100644
--- /dev/null
+++ b/git-jump.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE NoImplicitPrelude #-}
+import           Control.Exception (onException)
+import           Control.Monad (void)
+import           Data.List (isSuffixOf)
+import           System.Environment (getArgs, getProgName)
+import           System.Process
+
+import           Prelude.Compat
+
+chomp :: String -> String
+chomp str
+    | "\n" `isSuffixOf` str = init str
+    | otherwise = str
+
+git :: String -> [String] -> IO String
+git cmd args = readProcess "git" (cmd:args) ""
+
+git_ :: String -> [String] -> IO ()
+git_ cmd = void . git cmd
+
+revParse :: [String] -> IO String
+revParse args = chomp <$> git "rev-parse" args
+
+reset :: [String] -> IO ()
+reset = git_ "reset"
+
+logErrors :: IO a -> String -> IO a
+logErrors action msg = action `onException` putStrLn msg
+
+main :: IO ()
+main =
+    do
+        progName <- getProgName
+        args <- getArgs
+        destRef <-
+            case args of
+            [] -> revParse ["--symbolic-full-name", "@{u}"]
+            [refSpec] -> return refSpec
+            _ -> fail $ "Usage: " ++ progName ++ "[refspec]\n    if refspec is not provided, the remote tracked branch is used"
+        origPos <- revParse ["HEAD"]
+        git_ "commit" ["--allow-empty", "-mSTAGING"]
+        staging <- revParse ["HEAD"]
+        git_ "commit" ["--allow-empty", "-amUNSTAGED"]
+        unstaged <- revParse ["HEAD"]
+        let restore =
+                do
+                    reset ["--hard", unstaged]
+                    reset ["--mixed", staging]
+                    reset ["--soft", origPos]
+                    return ()
+        (`onException` restore) $
+            do
+                reset ["--hard", destRef]
+                    `logErrors` ("Failed to jump to " ++ show destRef)
+                git_ "cherry-pick" ["--allow-empty", staging]
+                git_ "cherry-pick" ["--allow-empty", unstaged]
+                reset ["--mixed", "HEAD^"]
+                reset ["--soft", "HEAD^"]
+                return ()
