packages feed

git-jump-0.1.0.0: git-jump.hs

{-# 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 ()