[#104] Support renames in 'hit status' (#121)

* [#104] Support renames in 'hit status'

* Improve documentation for parsing 'git diff'

* Remove unnecessary qualified use of words
This commit is contained in:
Curtis Chin Jen Sem 2019-10-09 06:33:41 +02:00 committed by Veronika Romashkina
parent ba0e52d3c5
commit 14b0e3df62
2 changed files with 98 additions and 32 deletions

View File

@ -191,7 +191,8 @@ runCurrent = do
{- | Show stats from the given commit. If commit is not specified, uses HEAD.
-}
runStatus :: Maybe Text -> IO ()
runStatus (fromMaybe "HEAD" -> commit) = withUntrackedFiles $ showPrettyDiff commit
runStatus (fromMaybe "HEAD" -> commit)
= withDeletedFiles $ withUntrackedFiles $ showPrettyDiff commit
{- | Show diff from the given commit. If commit is not specified, uses HEAD.
This commands checks whether @diff-hightligh@ is on path and if not, just calls
@ -260,22 +261,41 @@ issueFromBranch =
. T.drop 1
. T.dropWhile (/= '/')
{- | Perform the given action by first staging the given files and
later removing them again after the action
-}
withFiles :: IO [Text] -> IO a -> IO a
withFiles whichFiles action = bracket
addFiles
removeFiles
(const action)
where
addFiles :: IO [Text]
addFiles = do
files <- whichFiles
for_ files $ \file -> void $ "git" $| ["add", file]
pure files
-- Return files back to not spoil git state and have unexpected behavior
removeFiles :: [Text] -> IO ()
removeFiles = mapM_ $ \file -> void $ "git" $| ["reset", "--", file]
{- | Perform given action by adding all deleted files to index and returning
them back after action.
-}
withDeletedFiles :: IO a -> IO a
withDeletedFiles = withFiles deletedFiles
where
-- Find the deleted file to index so they will appear in diff
deletedFiles :: IO [Text]
deletedFiles = lines <$> "git" $| ["ls-files", "--deleted", "--exclude-standard"]
{- | Perform given action by adding all untracked files to index and returning
them back after action.
-}
withUntrackedFiles :: IO a -> IO a
withUntrackedFiles action = bracket
addUntrackedFiles
removeUntrackedFiles
(const action)
withUntrackedFiles = withFiles untrackedFiles
where
-- Add all untracked file to index so they will appear in diff
addUntrackedFiles :: IO [Text]
addUntrackedFiles = do
untrackedFiles <- lines <$> "git" $| ["ls-files", "--others", "--exclude-standard"]
for_ untrackedFiles $ \file -> void $ "git" $| ["add", file]
pure untrackedFiles
-- Return untracked files back to not spoil git state and have unexpected behavior
removeUntrackedFiles :: [Text] -> IO ()
removeUntrackedFiles = mapM_ $ \file -> void $ "git" $| ["reset", file]
-- Find the untracked file to index so they will appear in diff
untrackedFiles :: IO [Text]
untrackedFiles = lines <$> "git" $| ["ls-files", "--others", "--exclude-standard"]

View File

@ -27,19 +27,29 @@ data PatchType
| Unknown
| BrokenPairing
-- | Map conventional characters to 'PatchType'
{- | Parses the different change types.
Renames and copies contain an additional similarity percentage between the two files.
Potential values include:
'A' for newly added files
'M' for modified files
'R100' for renamed files, where 100 denotes a similarity percentage
'C75' for copied files, where 75 denotes a similarity percentage
-}
parsePatchType :: Text -> Maybe PatchType
parsePatchType = \case
"A" -> Just Added
"C" -> Just Copied
"D" -> Just Deleted
"M" -> Just Modified
"R" -> Just Renamed
"T" -> Just TypeChanged
"U" -> Just Unmerged
"X" -> Just Unknown
"B" -> Just BrokenPairing
_ -> Nothing
parsePatchType t = do
(c, _) <- T.uncons t
case c of
'A' -> Just Added
'C' -> Just Copied
'D' -> Just Deleted
'M' -> Just Modified
'R' -> Just Renamed
'T' -> Just TypeChanged
'U' -> Just Unmerged
'X' -> Just Unknown
'B' -> Just BrokenPairing
_ -> Nothing
-- | Display 'PatchType' in colorful and expanded text.
displayPatchType :: PatchType -> Text
@ -66,8 +76,25 @@ data DiffName = DiffName
, diffNameType :: !PatchType -- ^ type of the changed file
}
{- | Parses a diff list of file names.
When a file was renamed, both the previous and the new filename are given.
These could be in the following formats:
@
<patch-type> <filename>
<patch-type> <old-filename> <new-filename>
@
Typical raw text returned by @git@ can look like this:
@
M README.md
A foo
R100 bar baz
@
-}
parseDiffName :: [Text] -> Maybe DiffName
parseDiffName [t, name] = DiffName name <$> parsePatchType t
parseDiffName (t : xs) = DiffName (unwords xs) <$> parsePatchType t
parseDiffName _ = Nothing
-- | Output of the @git diff --stat@ command.
@ -87,14 +114,20 @@ It also handles special case of binary files. Typical raw text returned by @git@
can look like this:
@
.foo.un~ | Bin 0 -> 523 bytes
README.md | 4 ++++
foo | 1 +
.foo.un~ | Bin 0 -> 523 bytes
README.md | 4 ++++
foo | 1 +
bar => baz | 2 --
qux => quux | 0
@
-}
parseDiffStat :: [Text] -> Maybe DiffStat
parseDiffStat = \case
[diffStatFile, diffStatCount, diffStatSigns] -> Just DiffStat{..}
_:"=>":diffStatFile:diffStatCount:rest -> Just DiffStat
{ diffStatSigns = unwords rest
, ..
}
diffStatFile:"Bin":rest -> Just DiffStat
{ diffStatCount = "Bin"
, diffStatSigns = unwords rest
@ -122,7 +155,20 @@ showPrettyDiff commit = do
joinDiffs :: DiffName -> DiffStat -> (Text, Text, Text, Text)
joinDiffs DiffName{..} DiffStat{..} =
(displayPatchType diffNameType, diffNameFile, diffStatCount, diffStatSigns)
( displayPatchType diffNameType
, formatName diffNameType diffNameFile
, diffStatCount
, diffStatSigns
)
formatName :: PatchType -> Text -> Text
formatName = \case
Renamed -> formatRename
Copied -> formatRename
_ -> id
where
formatRename :: Text -> Text
formatRename = T.intercalate " -> " . words
formatTableAligned :: [(Text, Text, Text, Text)] -> Text
formatTableAligned rows = unlines $ map formatRow rows