1
1
mirror of https://github.com/github/semantic.git synced 2024-11-29 02:44:36 +03:00

Merge pull request #451 from github/git-patch-modes

Improve git patch headers
This commit is contained in:
Rob Rix 2016-02-22 16:42:45 -07:00
commit f6bd858248
15 changed files with 75 additions and 45 deletions

2
.gitmodules vendored
View File

@ -6,4 +6,4 @@
url = https://github.com/joshvera/text-icu
[submodule "vendor/gitlib"]
path = vendor/gitlib
url = https://github.com/jwiegley/gitlib
url = https://github.com/joshvera/gitlib

View File

@ -19,7 +19,7 @@ data Format = Unified | Split | Patch
data DiffArguments = DiffArguments { format :: Format, output :: Maybe FilePath, outputPath :: FilePath }
-- | Return a renderer from the command-line arguments that will print the diff.
printDiff :: Parser -> DiffArguments -> (Source Char, Source Char) -> IO ()
printDiff :: Parser -> DiffArguments -> (SourceBlob, SourceBlob) -> IO ()
printDiff parser arguments sources = case format arguments of
Unified -> put =<< diffFiles parser unified sources
where

View File

@ -1,6 +1,7 @@
{-# LANGUAGE RecordWildCards #-}
module Main where
import Source
import Data.Bifunctor.Join
import Diffing
import Options.Applicative
@ -20,8 +21,11 @@ arguments = Arguments
main :: IO ()
main = do
arguments <- execParser opts
sources <- sequence $ readAndTranscodeFile <$> Join (sourceA arguments, sourceB arguments)
DO.printDiff (parserForFilepath $ sourceA arguments) (args arguments) (runJoin sources)
let (sourceAPath, sourceBPath) = (sourceA arguments, sourceB arguments)
sources <- sequence $ readAndTranscodeFile <$> Join (sourceAPath, sourceBPath)
let srcs = runJoin sources
let sourceBlobs = (SourceBlob (fst srcs) mempty sourceAPath, SourceBlob (snd srcs) mempty sourceBPath)
DO.printDiff (parserForFilepath $ sourceA arguments) (args arguments) sourceBlobs
where opts = info (helper <*> arguments)
(fullDesc <> progDesc "Diff some things" <> header "semantic-diff - diff semantically")
args Arguments{..} = DO.DiffArguments { format = format, output = output, outputPath = sourceA }

View File

@ -6,6 +6,9 @@ import Source
import Options.Applicative
import qualified Data.ByteString.Char8 as B1
import qualified Data.Text as T
import Control.Monad
import Control.Arrow
import Data.Bifunctor
import Data.Bifunctor.Join
import Git.Libgit2
import Git.Types
@ -35,15 +38,17 @@ main = do
arguments@Arguments{..} <- execParser opts
let shas = Join (shaA, shaB)
forM_ filepaths $ \filepath -> do
sources <- sequence $ fetchFromGitRepo gitDir filepath <$> shas
DO.printDiff (parserForFilepath filepath) (args arguments filepath) (runJoin sources)
sourcesAndOids <- sequence $ fetchFromGitRepo gitDir filepath <$> shas
let (sources, oids)= (Join . join bimap fst $ runJoin sourcesAndOids, join bimap snd $ runJoin sourcesAndOids)
let sourceBlobs = (SourceBlob (fst $ runJoin sources) (fst oids) filepath, SourceBlob (snd $ runJoin sources) (snd oids) filepath)
DO.printDiff (parserForFilepath filepath) (args arguments filepath) sourceBlobs
where opts = info (helper <*> arguments)
(fullDesc <> progDesc "Diff some things" <> header "semantic-diff - diff semantically")
args Arguments{..} filepath = DO.DiffArguments { format = format, output = output, outputPath = filepath }
-- | Returns a file source given an absolute repo path, a relative file path, and the sha to look up.
fetchFromGitRepo :: FilePath -> FilePath -> String -> IO (Source Char)
fetchFromGitRepo repoPath path sha = join $ withRepository lgFactory repoPath $ do
fetchFromGitRepo :: FilePath -> FilePath -> String -> IO (Source Char, String)
fetchFromGitRepo repoPath path sha = withRepository lgFactory repoPath $ do
object <- unTagged <$> parseObjOid (T.pack sha)
commitIHope <- lookupObject object
commit <- case commitIHope of
@ -51,10 +56,12 @@ fetchFromGitRepo repoPath path sha = join $ withRepository lgFactory repoPath $
_ -> error "Expected commit SHA"
tree <- lookupTree (commitTree commit)
entry <- treeEntry tree (B1.pack path)
bytestring <- case entry of
Nothing -> return mempty
Just BlobEntry {..} -> do
blob <- lookupBlob blobEntryOid
let (BlobString s) = blobContents blob
return s
return $ transcode bytestring
(bytestring, oid) <- case entry of
Nothing -> return (mempty, mempty)
Just BlobEntry {..} -> do
blob <- lookupBlob blobEntryOid
let (BlobString s) = blobContents blob
let oid = renderObjOid $ blobOid blob
return (s, oid)
s <- liftIO $ transcode bytestring
return (s, T.unpack oid)

View File

@ -12,6 +12,7 @@ import Term
import TreeSitter
import Control.Comonad.Cofree
import Control.Arrow
import Data.Bifunctor.Join
import qualified Data.ByteString.Char8 as B1
import Data.Foldable
@ -68,8 +69,9 @@ readAndTranscodeFile path = do
-- | Given a parser and renderer, diff two sources and return the rendered
-- | result.
diffFiles :: Parser -> Renderer T.Text b -> (Source Char, Source Char) -> IO b
diffFiles parser renderer sources = do
terms <- sequence $ parser <$> Join sources
let replaceLeaves = breakDownLeavesByWord <$> Join sources
return $ renderer (uncurry diffTerms $ runJoin $ replaceLeaves <*> terms) sources
diffFiles :: Parser -> Renderer T.Text b -> (SourceBlob, SourceBlob) -> IO b
diffFiles parser renderer sourceBlobs = do
let sources = Join $ (source *** source) sourceBlobs
terms <- sequence $ parser <$> sources
let replaceLeaves = breakDownLeavesByWord <$> sources
return $ renderer (uncurry diffTerms $ runJoin $ replaceLeaves <*> terms) sourceBlobs

View File

@ -10,15 +10,16 @@ import Renderer
import Row
import Source hiding ((++), break)
import Split
import Control.Arrow
import Control.Comonad.Cofree
import Control.Monad.Free
import Data.Maybe
import Data.Monoid
import Data.Bifunctor
import Control.Monad
-- | Render a diff in the traditional patch format.
patch :: Renderer a String
patch diff (sourceA, sourceB) = mconcat $ showHunk (sourceA, sourceB) <$> hunks diff (sourceA, sourceB)
patch diff sources = mconcat $ showHunk sources <$> hunks diff sources
-- | A hunk in a patch, including the offset, changes, and context.
data Hunk a = Hunk { offset :: (Sum Int, Sum Int), changes :: [Change a], trailingContext :: [Row a] }
@ -46,8 +47,9 @@ lineLength EmptyLine = 0
lineLength _ = 1
-- | Given the before and after sources, render a hunk to a string.
showHunk :: (Source Char, Source Char) -> Hunk (SplitDiff a Info) -> String
showHunk sources hunk = header hunk ++ concat (showChange sources <$> changes hunk) ++ showLines (snd sources) ' ' (unRight <$> trailingContext hunk)
showHunk :: (SourceBlob, SourceBlob) -> Hunk (SplitDiff a Info) -> String
showHunk blobs@(beforeBlob, afterBlob) hunk = header blobs hunk ++ concat (showChange sources <$> changes hunk) ++ showLines (snd sources) ' ' (unRight <$> trailingContext hunk)
where sources = (source beforeBlob, source afterBlob)
-- | Given the before and after sources, render a change to a string.
showChange :: (Source Char, Source Char) -> Change (SplitDiff a Info) -> String
@ -69,16 +71,20 @@ getRange :: SplitDiff leaf Info -> Range
getRange (Free (Annotated (Info range _) _)) = range
getRange (Pure (Info range _ :< _)) = range
-- | Return the header for a hunk as a string.
header :: Hunk a -> String
header hunk = "@@ -" ++ show offsetA ++ "," ++ show lengthA ++ " +" ++ show offsetB ++ "," ++ show lengthB ++ " @@\n"
where (lengthA, lengthB) = getSum *** getSum $ hunkLength hunk
(offsetA, offsetB) = getSum *** getSum $ offset hunk
-- | Returns the header given two source blobs and a hunk.
header :: (SourceBlob, SourceBlob) -> Hunk a -> String
header blobs hunk = "diff --git a/" ++ path (fst blobs) ++ " b/" ++ path (snd blobs) ++ "\n" ++
"index " ++ oid (fst blobs) ++ ".." ++ oid (snd blobs) ++ "\n" ++
"@@ -" ++ show offsetA ++ "," ++ show lengthA ++ " +" ++ show offsetB ++ "," ++ show lengthB ++ " @@\n"
where (lengthA, lengthB) = join bimap getSum $ hunkLength hunk
(offsetA, offsetB) = join bimap getSum $ offset hunk
-- | Render a diff as a series of hunks.
hunks :: Renderer a [Hunk (SplitDiff a Info)]
hunks diff sources = hunksInRows (1, 1) . fst $ splitDiffByLines diff (0, 0) sources
hunks diff (beforeBlob, afterBlob) = hunksInRows (1, 1) . fst $ splitDiffByLines diff (0, 0) (before, after)
where
before = source beforeBlob
after = source afterBlob
-- | Given beginning line numbers, turn rows in a split diff into hunks in a
-- | patch.
hunksInRows :: (Sum Int, Sum Int) -> [Row (SplitDiff a Info)] -> [Hunk (SplitDiff a Info)]

View File

@ -4,4 +4,4 @@ import Diff
import Source
-- | A function that will render a diff, given the two source files.
type Renderer a b = Diff a Info -> (Source Char, Source Char) -> b
type Renderer a b = Diff a Info -> (SourceBlob, SourceBlob) -> b

View File

@ -4,6 +4,8 @@ import Range
import qualified Data.Vector as Vector
import qualified Data.Text as T
data SourceBlob = SourceBlob { source :: Source Char, oid :: String, path :: FilePath }
-- | The contents of a source file, backed by a vector for efficient slicing.
newtype Source a = Source { getVector :: Vector.Vector a }
deriving (Eq, Show, Functor, Foldable, Traversable)

View File

@ -45,7 +45,7 @@ styleName category = "category-" ++ case category of
-- | Render a diff as an HTML split diff.
split :: Renderer leaf TL.Text
split diff (before, after) = renderHtml
split diff (beforeBlob, afterBlob) = renderHtml
. docTypeHtml
. ((head $ link ! A.rel "stylesheet" ! A.href "style.css") <>)
. body
@ -53,6 +53,8 @@ split diff (before, after) = renderHtml
((colgroup $ (col ! A.width (stringValue . show $ columnWidth)) <> col <> (col ! A.width (stringValue . show $ columnWidth)) <> col) <>)
. mconcat $ numberedLinesToMarkup <$> reverse numbered
where
before = Source.source beforeBlob
after = Source.source afterBlob
rows = fst (splitDiffByLines diff (0, 0) (before, after))
numbered = foldl' numberRows [] rows
maxNumber = case numbered of

View File

@ -16,11 +16,10 @@ import Rainbow
-- | Render a diff with the unified format.
unified :: Renderer a [Chunk String]
unified diff (before, after) = fst $ iter g mapped
where
unified diff (beforeBlob, afterBlob) = fst $ iter g mapped where
mapped = fmap (unifiedPatch &&& range) diff
toChunk = chunk . toList
g (Annotated (_, info) syntax) = annotationAndSyntaxToChunks after info syntax
g (Annotated (_, info) syntax) = annotationAndSyntaxToChunks (source afterBlob) info syntax
-- | Render an annotation and syntax into a list of chunks.
annotationAndSyntaxToChunks source (Info range _) (Leaf _) = ([ toChunk $ slice range source ], Just range)
annotationAndSyntaxToChunks source (Info range _) (Indexed i) = (unifiedRange range i source, Just range)
@ -29,10 +28,11 @@ unified diff (before, after) = fst $ iter g mapped
-- | Render a Patch into a list of chunks.
unifiedPatch :: Patch (Term a Info) -> [Chunk String]
unifiedPatch patch = (fore red . bold <$> beforeChunks) <> (fore green . bold <$> afterChunks)
where
beforeChunks = maybe [] (change "-" . unifiedTerm before) $ Patch.before patch
afterChunks = maybe [] (change "+" . unifiedTerm after) $ Patch.after patch
unifiedPatch patch = (fore red . bold <$> beforeChunk) <> (fore green . bold <$> afterChunk) where
before = source beforeBlob
after = source afterBlob
beforeChunk = maybe [] (change "-" . unifiedTerm before) $ Patch.before patch
afterChunk = maybe [] (change "+" . unifiedTerm after) $ Patch.after patch
-- | Render the contents of a Term as a series of chunks.
unifiedTerm :: Source Char -> Term a Info -> [Chunk String]

View File

@ -6,6 +6,7 @@ import Renderer
import Split
import Unified
import qualified Source as S
import Control.DeepSeq
import Data.Bifunctor.Join
import qualified Data.ByteString.Char8 as B1
@ -78,7 +79,9 @@ testDiff :: Renderer T.Text String -> FilePath -> FilePath -> Maybe FilePath ->
testDiff renderer a b diff matcher = do
let parser = parserForFilepath a
sources <- sequence $ readAndTranscodeFile <$> Join (a, b)
actual <- diffFiles parser renderer (runJoin sources)
let srcs = runJoin sources
let sourceBlobs = (S.SourceBlob (fst srcs) mempty a, S.SourceBlob (snd srcs) mempty b)
actual <- diffFiles parser renderer sourceBlobs
case diff of
Nothing -> actual `deepseq` matcher (actual, actual)
Just file -> do

View File

@ -9,7 +9,7 @@ import Control.Monad.Free
import Test.Hspec
spec :: Spec
spec = parallel $ do
describe "hunks" $ do
spec = parallel $
describe "hunks" $
it "empty diffs have no hunks" $
hunks (Free . Annotated (Info (Range 0 0) mempty, Info (Range 0 0) mempty) $ Leaf "") (fromList "", fromList "") `shouldBe` []
hunks (Free . Annotated (Info (Range 0 0) mempty, Info (Range 0 0) mempty) $ Leaf "") (SourceBlob (fromList "") "abcde" "path2.txt", SourceBlob (fromList "") "xyz" "path2.txt") `shouldBe` []

View File

@ -1,3 +1,5 @@
diff --git a/test/diffs/newline-at-eof.A.js b/test/diffs/newline-at-eof.B.js
index ..
@@ -1,2 +1,4 @@
console.log("hello, world");

View File

@ -1,3 +1,5 @@
diff --git a/test/diffs/no-newline-at-eof.A.js b/test/diffs/no-newline-at-eof.B.js
index ..
@@ -1,1 +1,3 @@
console.log("hello, world");

2
vendor/gitlib vendored

@ -1 +1 @@
Subproject commit b0c3ad9a7d453fce30364b4a277799c5e2f26947
Subproject commit 3b65d9f22ef4a1bc14c3b1d35d819ef9d29878b1