git merging works
This commit is contained in:
		
							parent
							
								
									bc5d7a6915
								
							
						
					
					
						commit
						60a08808b9
					
				
							
								
								
									
										102
									
								
								Main.hs
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								Main.hs
									
									
									
									
									
								
							|  | @ -5,7 +5,10 @@ module Main where | |||
| import Control.Exception | ||||
| import Control.Monad | ||||
| import Data.Bool | ||||
| import Data.Char | ||||
| import Data.Foldable | ||||
| import Data.List | ||||
| import Data.Traversable | ||||
| import Opts | ||||
| import System.Exit | ||||
| import System.FilePath | ||||
|  | @ -36,6 +39,59 @@ rundiff f1 f2 out = do | |||
|   unless (st `elem` [ExitSuccess, ExitFailure 1]) | ||||
|     $ error "diff failed for unknown reason (is GNU diffutils installed?)" | ||||
| 
 | ||||
| gitRepoRelRoot = do | ||||
|   (path, st) <- | ||||
|     withCreateProcess | ||||
|       (proc "git" ["rev-parse", "--show-cdup"]) | ||||
|         {std_in = NoStream, std_out = CreatePipe, std_err = Inherit} $ \_ (Just oh) _ p -> | ||||
|       (,) <$> hGetContents' oh <*> waitForProcess p | ||||
|   unless (st == ExitSuccess) $ error "git failed" | ||||
|   let [p] = lines path | ||||
|   pure p | ||||
| 
 | ||||
| gitUnmerged = do | ||||
|   (paths, st) <- | ||||
|     withCreateProcess | ||||
|       (proc "git" ["status", "--porcelain=v1"]) | ||||
|         {std_in = NoStream, std_out = CreatePipe, std_err = Inherit} $ \_ (Just oh) _ p -> | ||||
|       (,) | ||||
|         <$> (map (drop 3) . filter ("UU " `isPrefixOf`) . lines | ||||
|                <$> hGetContents' oh) | ||||
|         <*> waitForProcess p | ||||
|   unless (st == ExitSuccess) $ error "git failed" | ||||
|   pure paths | ||||
| 
 | ||||
| gitCheckoutMOY u my old your = do | ||||
|   (paths, st) <- | ||||
|     withCreateProcess | ||||
|       (proc "git" ["ls-files", "--unmerged", "--", u]) | ||||
|         {std_in = NoStream, std_out = CreatePipe, std_err = Inherit} $ \_ (Just oh) _ p -> | ||||
|       (,) | ||||
|         <$> (sortOn snd | ||||
|                . map ((\[a, b] -> (a, b)) . take 2 . drop 1 . words) | ||||
|                . lines | ||||
|                <$> hGetContents' oh) | ||||
|         <*> waitForProcess p | ||||
|   unless (st == ExitSuccess) $ error "git failed" | ||||
|   let co (hash, _) path = do | ||||
|         st <- | ||||
|           withCreateProcess | ||||
|             (proc "git" ["cat-file", "blob", hash]) | ||||
|               {std_in = NoStream, std_out = CreatePipe, std_err = Inherit} $ \_ (Just ho) _ p -> do | ||||
|             hGetContents ho >>= writeFile path . Toks.split -- TODO cfg | ||||
|             waitForProcess p | ||||
|         unless (st == ExitSuccess) . error | ||||
|           $ "failed checking out " ++ u ++ " from blob " ++ hash | ||||
|   case paths of | ||||
|     [(_, "1"), (_, "2"), (_, "3")] -> | ||||
|       zipWithM co paths [old, my, your] >> pure () | ||||
|     _ -> error $ "bad data from ls-files for unmerged " ++ u | ||||
| 
 | ||||
| gitAdd path = do | ||||
|   traceM $ "adding " ++ path | ||||
|   st <- rawSystem "git" ["add", "--", path] | ||||
|   unless (st == ExitSuccess) $ error "git-add failed" | ||||
| 
 | ||||
| data Op | ||||
|   = Del | ||||
|   | Keep | ||||
|  | @ -125,6 +181,14 @@ resolve _ x = x | |||
| -- separate/overlapped conflict resolution -- e.g., what if someone wants to | ||||
| -- merge overlapping edits in text but separate edits in spaces? At this point | ||||
| -- that might be ignorable. | ||||
| -- | ||||
| -- Also, conflicts that are made out of an ignorable space change and a | ||||
| -- mergeable non-space change now cause conflicts because the spaces are no | ||||
| -- longer truly separable/alignable here. Ideally some part of the space | ||||
| -- merging should be done at alignment (e.g., fake all spaces to cause them to | ||||
| -- align well). Also it might be necessary to group space-tokens together | ||||
| -- (newline and indent are now 2 space tokens, which won't ever merge with a | ||||
| -- single space) | ||||
| resolveSpace Config {..} c@(Conflict m o y) | ||||
|   | m == o && o == y = Ok o | ||||
|   | otherwise = | ||||
|  | @ -140,21 +204,22 @@ resolveSpaces _ x = x | |||
| merge cfg@Config {..} ms ys = | ||||
|   regroup | ||||
|     . map (resolve cfg) | ||||
|     . traceShowId | ||||
|     . regroup | ||||
|     . bool id (concatMap zeal) cfgZealous | ||||
|     . expand cfgContext | ||||
|     . regroup | ||||
|     $ align ms ys | ||||
| 
 | ||||
| format :: Config -> [Merged] -> IO Bool | ||||
| format Config {..} = go False | ||||
| format :: Config -> Handle -> [Merged] -> IO Bool | ||||
| format Config {..} h = go False | ||||
|   where | ||||
|     go c [] = pure c | ||||
|     go c (Ok x:xs) = do | ||||
|       putStr (Toks.glueToks x) | ||||
|       hPutStr h (Toks.glueToks x) | ||||
|       go c xs | ||||
|     go c (Conflict m o y:xs) = do | ||||
|       putStr | ||||
|       hPutStr h | ||||
|         $ mconcat | ||||
|             [ cfgLabelStart | ||||
|             , Toks.glueToks m | ||||
|  | @ -174,11 +239,36 @@ runCmd CmdDiff3 {..} cfg = | |||
|       readFile path >>= writeFile tmp . Toks.split -- TODO cfg | ||||
|     rundiff fOld fMy fdMy | ||||
|     rundiff fOld fYour fdYour | ||||
|     conflicted <- merge cfg <$> pdiff fdMy <*> pdiff fdYour >>= format cfg | ||||
|     conflicted <- | ||||
|       merge cfg <$> pdiff fdMy <*> pdiff fdYour >>= format cfg stdout | ||||
|     if conflicted | ||||
|       then exitWith (ExitFailure 1) | ||||
|       else exitSuccess | ||||
| runCmd _ _ = error "not implemented yet" | ||||
| runCmd CmdGitMerge {..} cfg = do | ||||
|   relroot <- gitRepoRelRoot | ||||
|   unmerged <- | ||||
|     case gmFiles of | ||||
|       Nothing -> map (relroot </>) <$> gitUnmerged | ||||
|       Just fs -> pure fs | ||||
|   conflicts <- | ||||
|     for unmerged $ \u -> | ||||
|       withSystemTempDirectory "werge-git" $ \workdir -> do | ||||
|         let [fMy, fOld, fYour, fdMy, fdYour] = | ||||
|               map (workdir </>) ["my", "old", "your", "mydiff", "yourdiff"] | ||||
|         gitCheckoutMOY u fMy fOld fYour | ||||
|         rundiff fOld fMy fdMy | ||||
|         rundiff fOld fYour fdYour | ||||
|         readFile u >>= writeFile (u ++ ".werge-backup") | ||||
|         conflict <- | ||||
|           withFile u WriteMode $ \h -> | ||||
|             merge cfg <$> pdiff fdMy <*> pdiff fdYour >>= format cfg h | ||||
|         traceShowM conflict | ||||
|         traceShowM gmDoAdd | ||||
|         unless conflict $ when gmDoAdd $ gitAdd u | ||||
|         pure conflict | ||||
|   if or conflicts | ||||
|     then exitWith (ExitFailure 1) | ||||
|     else exitSuccess | ||||
| 
 | ||||
| main :: IO () | ||||
| main = catch go bad | ||||
|  |  | |||
							
								
								
									
										26
									
								
								Opts.hs
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								Opts.hs
									
									
									
									
									
								
							|  | @ -143,9 +143,9 @@ data Command | |||
|       , d3old :: FilePath | ||||
|       , d3your :: FilePath | ||||
|       } | ||||
|   | CmdGitMergetool | ||||
|       { gmtFiles :: Maybe [FilePath] | ||||
|       , gmtDoAdd :: Bool | ||||
|   | CmdGitMerge | ||||
|       { gmFiles :: Maybe [FilePath] | ||||
|       , gmDoAdd :: Bool | ||||
|       } | ||||
|   deriving (Show) | ||||
| 
 | ||||
|  | @ -156,31 +156,33 @@ cmdDiff3 = do | |||
|     strArgument $ metavar "YOURFILE" <> help "version with other people's edits" | ||||
|   pure CmdDiff3 {..} | ||||
| 
 | ||||
| cmdGitMergetool = do | ||||
|   gmtFiles <- | ||||
| cmdGitMerge = do | ||||
|   gmFiles <- | ||||
|     asum | ||||
|       [ fmap Just . many | ||||
|       [ fmap Just . some | ||||
|           $ strArgument | ||||
|           $ metavar "UNMERGED" | ||||
|               <> help "unmerged git file (can be specified repeatedly" | ||||
|               <> help "unmerged git file (can be specified repeatedly)" | ||||
|       , flag' | ||||
|           Nothing | ||||
|           (long "unmerged" | ||||
|              <> short 'u' | ||||
|              <> help "process all files marked as unmerged by git") | ||||
|       ] | ||||
|   gmtDoAdd <- | ||||
|   gmDoAdd <- | ||||
|     asum | ||||
|       [ flag' | ||||
|           False | ||||
|           True | ||||
|           (long "add" | ||||
|              <> short 'a' | ||||
|              <> help "run `git add' for fully merged files") | ||||
|       , flag' True (long "no-add" <> help "prevent running `git add'") | ||||
|       , flag' False (long "no-add" <> help "prevent running `git add'") | ||||
|       , pure False | ||||
|       ] | ||||
|   pure CmdGitMergetool {..} | ||||
|   pure CmdGitMerge {..} | ||||
| 
 | ||||
| -- TODO have some option to output the (partially merged) my/old/your files so | ||||
| -- that folks can continue with external program or so (such as meld) | ||||
| cmd = | ||||
|   hsubparser | ||||
|     $ mconcat | ||||
|  | @ -188,7 +190,7 @@ cmd = | |||
|             $ info cmdDiff3 | ||||
|             $ progDesc "diff3-style merge of changes" | ||||
|         , command "git" | ||||
|             $ info cmdGitMergetool | ||||
|             $ info cmdGitMerge | ||||
|             $ progDesc "try to merge unmerged git tree" | ||||
|         ] | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue